{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "slider",
  "title": "Slider",
  "type": "registry:ui",
  "files": [
    {
      "path": "packages/registry-react/components/slider/Slider.tsx",
      "content": "'use client'\n\nimport * as React from 'react'\nimport * as SliderPrimitive from '@radix-ui/react-slider'\nimport * as TooltipPrimitive from '@radix-ui/react-tooltip'\nimport { cn } from '@/lib/utils'\n\nexport interface SliderMark {\n  label: string\n  style?: React.CSSProperties\n}\n\nexport interface SliderProps\n  extends Omit<\n    React.ComponentPropsWithoutRef<typeof SliderPrimitive.Root>,\n    'value' | 'defaultValue' | 'inverted' | 'orientation' | 'onValueChange' | 'onValueCommit'\n  > {\n  className?: string\n  /** Controlled value (Radix uses number[]). */\n  value?: number[]\n  /** Uncontrolled initial value. */\n  defaultValue?: number[]\n  onValueChange?: (value: number[]) => void\n  onValueCommit?: (value: number[]) => void\n  /** Enable dual-thumb range selection (affects default value when uncontrolled). */\n  range?: boolean\n  /** Vertical orientation */\n  vertical?: boolean\n  /** Height when vertical (px or css value) */\n  height?: string | number\n  /** Tick marks with labels */\n  marks?: Record<number, string | SliderMark>\n  /** Show tooltip on drag/focus. Boolean or formatter function */\n  tooltip?: boolean | ((value: number) => string)\n  /** Show dots at each step */\n  dots?: boolean\n  /** Reverse direction (right-to-left or bottom-to-top) */\n  reverse?: boolean\n  /** Highlight the track between thumbs/min (default true) */\n  included?: boolean\n  /** Size variant */\n  size?: 'small' | 'default'\n}\n\nconst Slider = React.forwardRef<React.ElementRef<typeof SliderPrimitive.Root>, SliderProps>(\n  (\n    {\n      className,\n      value,\n      defaultValue,\n      onValueChange,\n      onValueCommit,\n      min = 0,\n      max = 100,\n      step = 1,\n      range = false,\n      vertical = false,\n      height,\n      marks,\n      tooltip = true,\n      dots = false,\n      reverse = false,\n      included = true,\n      size = 'default',\n      ...props\n    },\n    ref,\n  ) => {\n    /* ── normalize value to number[] ── */\n    const resolvedDefault = React.useMemo<number[]>(() => {\n      if (defaultValue != null) return defaultValue\n      return range ? [min, max] : [min]\n    }, [defaultValue, range, min, max])\n\n    // Track current values so tooltips/marks can read them in both controlled\n    // and uncontrolled usage (Radix doesn't surface the value to children).\n    const [internalValue, setInternalValue] = React.useState<number[]>(resolvedDefault)\n    const isControlled = value !== undefined\n    const currentValue = isControlled ? value! : internalValue\n\n    const handleChange = React.useCallback(\n      (next: number[]) => {\n        if (!isControlled) setInternalValue(next)\n        onValueChange?.(next)\n      },\n      [isControlled, onValueChange],\n    )\n\n    /* ── layout helpers ── */\n    const orientation = vertical ? 'vertical' : 'horizontal'\n    const isHorizontal = orientation === 'horizontal'\n\n    const trackSize =\n      size === 'small'\n        ? isHorizontal\n          ? 'h-1'\n          : 'w-1'\n        : isHorizontal\n          ? 'h-1.5'\n          : 'w-1.5'\n\n    const thumbSize = size === 'small' ? 'size-3' : 'size-4'\n\n    /* ── marks ── */\n    const markList = React.useMemo(() => {\n      if (!marks) return []\n      const entries = Object.entries(marks).map(([key, val]) => {\n        const num = Number(key)\n        const label = typeof val === 'string' ? val : val.label\n        const style = typeof val === 'string' ? undefined : val.style\n        const pct = ((num - min) / (max - min)) * 100\n        return { value: num, label, style, pct }\n      })\n      entries.sort((a, b) => a.value - b.value)\n      return entries\n    }, [marks, min, max])\n\n    /* ── step dots ── */\n    const dotList = React.useMemo(() => {\n      if (!dots) return []\n      const list: number[] = []\n      const count = Math.floor((max - min) / step)\n      for (let i = 0; i <= count; i++) {\n        list.push(min + i * step)\n      }\n      return list\n    }, [dots, max, min, step])\n\n    /* ── tooltip ── */\n    const showTooltip = tooltip !== false\n    const formatTooltip = (v: number) =>\n      typeof tooltip === 'function' ? tooltip(v) : String(v)\n\n    const thumbClass = cn(\n      'border-primary bg-background ring-ring/50 block shrink-0 rounded-full border shadow-sm transition-[color,box-shadow] hover:ring-4 focus-visible:ring-4 focus-visible:outline-hidden disabled:pointer-events-none disabled:opacity-50',\n      thumbSize,\n    )\n\n    return (\n      <TooltipPrimitive.Provider>\n        <div\n          className={cn('relative w-full', !isHorizontal && 'flex flex-col items-center')}\n          style={\n            !isHorizontal && height\n              ? { height: typeof height === 'number' ? `${height}px` : height }\n              : undefined\n          }\n        >\n          <SliderPrimitive.Root\n            ref={ref}\n            data-uipkge=\"\"\n            data-slot=\"slider\"\n            min={min}\n            max={max}\n            step={step}\n            value={isControlled ? value : undefined}\n            defaultValue={isControlled ? undefined : resolvedDefault}\n            orientation={orientation}\n            inverted={reverse}\n            onValueChange={handleChange}\n            onValueCommit={onValueCommit}\n            className={cn(\n              'relative flex touch-none select-none data-[disabled]:opacity-50',\n              isHorizontal ? 'w-full items-center' : 'h-full min-h-44 flex-col justify-center',\n              className,\n            )}\n            {...props}\n          >\n            {/* Track */}\n            <SliderPrimitive.Track\n              data-uipkge=\"\"\n              data-slot=\"slider-track\"\n              className={cn('bg-muted relative grow overflow-hidden rounded-full', trackSize)}\n            >\n              {included && (\n                <SliderPrimitive.Range\n                  data-uipkge=\"\"\n                  data-slot=\"slider-range\"\n                  className={cn('bg-primary absolute', isHorizontal ? 'h-full' : 'w-full')}\n                />\n              )}\n            </SliderPrimitive.Track>\n\n            {/* Step dots */}\n            {dotList.map((dot) => (\n              <div\n                key={dot}\n                className={cn(\n                  'border-primary/40 bg-background absolute rounded-full border',\n                  isHorizontal\n                    ? 'top-1/2 size-1.5 -translate-y-1/2'\n                    : 'left-1/2 size-1.5 -translate-x-1/2',\n                  size === 'small' && 'size-1',\n                )}\n                style={\n                  isHorizontal\n                    ? {\n                        left: `${((dot - min) / (max - min)) * 100}%`,\n                        transform: 'translateX(-50%) translateY(-50%)',\n                      }\n                    : {\n                        bottom: `${((dot - min) / (max - min)) * 100}%`,\n                        transform: 'translateX(-50%) translateY(50%)',\n                      }\n                }\n              />\n            ))}\n\n            {/* Marks */}\n            {markList.length > 0 && (\n              <div\n                className={cn(\n                  'pointer-events-none absolute',\n                  isHorizontal ? 'top-full mt-2.5 h-5 w-full' : 'top-0 left-full ml-3 h-full w-20',\n                )}\n              >\n                {markList.map((mark) => (\n                  <span\n                    key={mark.value}\n                    className=\"text-muted-foreground absolute text-xs whitespace-nowrap\"\n                    style={{\n                      ...mark.style,\n                      ...(isHorizontal\n                        ? { left: `${mark.pct}%`, transform: 'translateX(-50%)' }\n                        : { bottom: `${mark.pct}%`, transform: 'translateY(50%)' }),\n                    }}\n                  >\n                    {mark.label}\n                  </span>\n                ))}\n              </div>\n            )}\n\n            {/* Thumbs (with tooltips) */}\n            {currentValue.map((thumbValue, idx) =>\n              showTooltip ? (\n                <TooltipPrimitive.Root key={idx} delayDuration={0}>\n                  <TooltipPrimitive.Trigger asChild>\n                    <SliderPrimitive.Thumb\n                      data-uipkge=\"\"\n                      data-slot=\"slider-thumb\"\n                      className={thumbClass}\n                    />\n                  </TooltipPrimitive.Trigger>\n                  <TooltipPrimitive.Portal>\n                    <TooltipPrimitive.Content\n                      side={isHorizontal ? 'top' : 'right'}\n                      sideOffset={4}\n                      className=\"bg-foreground text-background z-50 w-fit rounded-md px-2 py-1 text-xs\"\n                    >\n                      {formatTooltip(thumbValue ?? 0)}\n                      <TooltipPrimitive.Arrow className=\"bg-foreground fill-foreground size-2.5 rotate-45 rounded-[2px]\" />\n                    </TooltipPrimitive.Content>\n                  </TooltipPrimitive.Portal>\n                </TooltipPrimitive.Root>\n              ) : (\n                <SliderPrimitive.Thumb\n                  key={idx}\n                  data-uipkge=\"\"\n                  data-slot=\"slider-thumb\"\n                  className={thumbClass}\n                />\n              ),\n            )}\n          </SliderPrimitive.Root>\n        </div>\n      </TooltipPrimitive.Provider>\n    )\n  },\n)\nSlider.displayName = 'Slider'\n\nexport { Slider }\n",
      "type": "registry:ui",
      "target": "~/components/ui/slider/Slider.tsx"
    },
    {
      "path": "packages/registry-react/components/slider/index.ts",
      "content": "export { Slider, type SliderProps, type SliderMark } from './Slider'\n",
      "type": "registry:ui",
      "target": "~/components/ui/slider/index.ts"
    }
  ],
  "dependencies": [
    "@radix-ui/react-slider",
    "@radix-ui/react-tooltip"
  ],
  "devDependencies": [],
  "registryDependencies": [],
  "description": "Single-thumb slider — pick a value within a range. Optional tick marks, step size, and inline value display.",
  "categories": [
    "form"
  ]
}