{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "range-selection-slider",
  "type": "registry:component",
  "title": "Range Selection Slider",
  "description": "A responsive range selection slider widget that allows users to precisely choose values, enhanced with smooth interactions and immediate visual feedback for accurate control.",
  "dependencies": [
    "motion"
  ],
  "files": [
    {
      "path": "components/watermelon/range-selection-slider.tsx",
      "type": "registry:component",
      "content": "'use client';\n\nimport React, { type FC, useState, useRef, useCallback } from 'react';\nimport { motion, useMotionValue, useTransform } from 'motion/react';\n\ninterface DigitColumnProps {\n  digit: number;\n  height: number;\n}\n\ninterface RollingNumberProps {\n  value: number;\n  prefix?: string;\n  fontSizeClass?: string;\n}\n\ninterface RangeSliderProps {\n  min: number;\n  max: number;\n  step?: number;\n  value: [number, number];\n  onChange: (value: [number, number]) => void;\n}\n\nexport interface PriceRangeCardProps {\n  defaultRange?: [number, number];\n  min?: number;\n  max?: number;\n  step?: number;\n  prefix?: string;\n  onApply?: (range: [number, number]) => void;\n  onCancel?: (range: [number, number]) => void;\n}\n\ntype DragType = 'min' | 'max' | null;\n\nfunction cn(...classes: Array<string | false | undefined>) {\n  return classes.filter(Boolean).join(' ');\n}\n\nconst DigitColumn: FC<DigitColumnProps> = ({ digit, height }) => {\n  return (\n    <div\n      className=\"relative overflow-hidden\"\n      style={{ height: height, width: '0.65em' }}\n    >\n      <motion.div\n        animate={{ y: -digit * height }}\n        transition={{\n          type: 'spring',\n          stiffness: 140,\n          damping: 22,\n          mass: 0.6,\n        }}\n        className=\"absolute top-0 left-0 flex w-full flex-col items-center\"\n      >\n        {Array.from({ length: 10 }).map((_, i) => (\n          <span\n            key={i}\n            style={{ height: height }}\n            className=\"flex w-full items-center justify-center\"\n          >\n            {i}\n          </span>\n        ))}\n      </motion.div>\n    </div>\n  );\n};\n\nexport const RollingNumber: FC<RollingNumberProps> = ({\n  value,\n  prefix = '',\n}) => {\n  const formatted = prefix + value.toLocaleString();\n  const height =\n    typeof window !== 'undefined' && window.innerWidth < 640 ? 24 : 32;\n\n  return (\n    <div\n      className={cn(\n        'flex items-center leading-none font-bold text-[#010103] tabular-nums dark:text-neutral-100',\n        'h-[24px] sm:h-[32px]',\n      )}\n    >\n      {formatted.split('').map((char, index) => {\n        const isNumber = !isNaN(parseInt(char, 10));\n\n        if (!isNumber) {\n          return (\n            <span key={index} className=\"px-px\">\n              {char}\n            </span>\n          );\n        }\n\n        return <DigitColumn key={index} digit={Number(char)} height={height} />;\n      })}\n    </div>\n  );\n};\n\nconst RangeSlider: FC<RangeSliderProps> = ({\n  min,\n  max,\n  step = 1,\n  value,\n  onChange,\n}) => {\n  const trackRef = useRef<HTMLDivElement>(null);\n  const dragging = useRef<DragType>(null);\n\n  const percentFromValue = useCallback(\n    (v: number) => ((v - min) / (max - min)) * 100,\n    [min, max],\n  );\n\n  const valueFromX = useCallback(\n    (x: number) => {\n      if (!trackRef.current) return min;\n      const rect = trackRef.current.getBoundingClientRect();\n      const percent = Math.min(1, Math.max(0, (x - rect.left) / rect.width));\n      const raw = min + percent * (max - min);\n      return Math.round(raw / step) * step;\n    },\n    [min, max, step],\n  );\n\n  const minPercent = useMotionValue(0);\n  const maxPercent = useMotionValue(0);\n\n  const minP = percentFromValue(value[0]);\n  const maxP = percentFromValue(value[1]);\n\n  if (minPercent.get() !== minP) {\n    minPercent.set(minP);\n  }\n\n  if (maxPercent.get() !== maxP) {\n    maxPercent.set(maxP);\n  }\n\n  const rangeLeft = useTransform(minPercent, (v) => `${v}%`);\n  const rangeWidth = useTransform([minPercent, maxPercent], (latest) => {\n    const minV = Number(latest[0]);\n    const maxV = Number(latest[1]);\n    return `${maxV - minV}%`;\n  });\n\n  const thumbMinLeft = useTransform(minPercent, (v) => `calc(${v}% - 16px)`);\n  const thumbMaxLeft = useTransform(maxPercent, (v) => `calc(${v}% - 16px)`);\n\n  const handleMove = (e: React.PointerEvent) => {\n    if (!dragging.current) return;\n\n    const newValue = valueFromX(e.clientX);\n\n    if (dragging.current === 'min') {\n      const clamped = Math.min(newValue, value[1] - step);\n      onChange([clamped, value[1]]);\n    } else {\n      const clamped = Math.max(newValue, value[0] + step);\n      onChange([value[0], clamped]);\n    }\n  };\n\n  const stop = () => {\n    dragging.current = null;\n  };\n\n  return (\n    <div\n      className=\"relative flex h-14 w-full touch-none items-center select-none\"\n      onPointerMove={handleMove}\n      onPointerUp={stop}\n      onPointerLeave={stop}\n    >\n      <div\n        ref={trackRef}\n        className=\"absolute h-2 w-full rounded-full bg-gray-200 dark:bg-neutral-800\"\n      >\n        <motion.div\n          className=\"absolute h-full rounded-full bg-neutral-800 dark:bg-neutral-300\"\n          style={{\n            left: rangeLeft,\n            width: rangeWidth,\n          }}\n        />\n      </div>\n\n      <motion.div\n        onPointerDown={(e) => {\n          (e.target as HTMLElement).setPointerCapture(e.pointerId);\n          dragging.current = 'min';\n        }}\n        className=\"absolute h-8 w-8 cursor-grab rounded-full border-[6px] border-[#010103] bg-[#FEFEFE] shadow-2xl active:cursor-grabbing dark:border-neutral-300 dark:bg-neutral-800\"\n        style={{ left: thumbMinLeft }}\n      />\n\n      <motion.div\n        onPointerDown={(e) => {\n          (e.target as HTMLElement).setPointerCapture(e.pointerId);\n          dragging.current = 'max';\n        }}\n        className=\"absolute h-8 w-8 cursor-grab rounded-full border-[6px] border-[#010103] bg-[#FEFEFE] shadow-2xl active:cursor-grabbing dark:border-neutral-300 dark:bg-neutral-800\"\n        style={{ left: thumbMaxLeft }}\n      />\n    </div>\n  );\n};\n\nexport const PriceRangeCard: FC<PriceRangeCardProps> = ({\n  defaultRange = [800, 2400],\n  min = 0,\n  max = 5000,\n  step = 20,\n  prefix = '$',\n  onApply,\n  onCancel,\n}) => {\n  const [range, setRange] = useState<[number, number]>(defaultRange);\n\n  return (\n    <div className=\"w-full w-xs overflow-hidden rounded-[2rem] border border-[#F0F0F0] bg-[#FEFEFE] shadow-xl sm:w-sm sm:max-w-sm dark:border-neutral-800 dark:bg-neutral-900\">\n      <div className=\"flex flex-col gap-4 p-5 sm:p-6\">\n        <h2 className=\"text-xl font-extrabold tracking-tight text-[#010103] dark:text-neutral-100\">\n          Price Range\n        </h2>\n\n        <RangeSlider\n          min={min}\n          max={max}\n          step={step}\n          value={range}\n          onChange={setRange}\n        />\n\n        <div className=\"mt-2 flex flex-col gap-3 sm:gap-4\">\n          {(['From', 'To'] as const).map((label, i) => (\n            <div\n              key={label}\n              className=\"flex flex-col gap-1 rounded-2xl bg-[#F4F4FB] p-4 dark:bg-neutral-800/50\"\n            >\n              <span className=\"text-[10px] font-bold tracking-wider text-[#76767D] uppercase sm:text-xs dark:text-neutral-500\">\n                {label}\n              </span>\n              <div className=\"text-xl font-bold sm:text-2xl\">\n                <RollingNumber value={range[i]} prefix={prefix} />\n              </div>\n            </div>\n          ))}\n        </div>\n      </div>\n\n      <div className=\"flex gap-3 px-5 pt-2 pb-6 sm:gap-4 sm:px-6\">\n        <button\n          className=\"flex-1 rounded-full bg-[#000002] py-2.5 text-sm text-[#FEFEFE] active:scale-95 sm:text-base dark:bg-neutral-100 dark:text-neutral-950\"\n          onClick={() => onApply?.(range)}\n        >\n          Apply\n        </button>\n\n        <button\n          onClick={() => {\n            setRange(defaultRange);\n            onCancel?.(defaultRange);\n          }}\n          className=\"flex-1 rounded-full border border-[#E4E4E9] py-2.5 text-sm font-bold text-[#69686F] hover:bg-gray-50 active:scale-95 sm:text-base dark:border-neutral-700 dark:text-neutral-400 dark:hover:bg-neutral-800\"\n        >\n          Cancel\n        </button>\n      </div>\n    </div>\n  );\n};\n"
    }
  ]
}
