{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "slider-tone",
  "title": "Tone slider",
  "description": "Styled slider: the Tone set. 5 decorative slider variations (InfoSlider, SuccessSlider, WarningSlider, DangerSlider, MutedSlider) on the ai2 token system, powered by framer-motion, reduced-motion aware and sized sm to xl. Part of the free styled layer.",
  "dependencies": [
    "motion@^12.42.2"
  ],
  "registryDependencies": [
    "@ai2/tokens"
  ],
  "files": [
    {
      "path": "registry/ai2/styled/slider-tone.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { motion, useReducedMotion } from \"motion/react\"\n\nimport { cn } from \"@/lib/utils\"\n\n/* Slider tone family: 5 semantically toned sliders (info / success / warning /\n   danger / muted). A self-sufficient structure: a track div + a fill div + a thumb\n   button. Pointer dragging (clientX within the track rect, with capture on the\n   track) AND keyboard (arrows, Shift+arrow for a large step, Home/End). Color comes\n   ONLY from tokens, via alpha color-mix. Every root carries data-tone; the invalid\n   appearance uses the ai2 danger token. */\n\nexport type StyledSize = \"sm\" | \"md\" | \"lg\" | \"xl\"\n\ntype Tone = \"info\" | \"success\" | \"warning\" | \"danger\" | \"muted\"\n\nconst trackHeight: Record<StyledSize, string> = {\n  sm: \"h-1\",\n  md: \"h-1.5\",\n  lg: \"h-2.5\",\n  xl: \"h-3.5\",\n}\nconst thumbSize: Record<StyledSize, string> = {\n  sm: \"size-3.5\",\n  md: \"size-4\",\n  lg: \"size-5\",\n  xl: \"size-6\",\n}\n\nconst rootBase =\n  \"relative flex w-56 max-w-full touch-none select-none items-center outline-none\"\n/* Basparmak 24px altinda: gorunmez dokunma alani genisletmesi zorunlu. */\nconst thumbBase =\n  \"absolute top-1/2 -translate-x-1/2 -translate-y-1/2 rounded-full border-2 border-background shadow-sm outline-none after:absolute after:-inset-1.5 after:content-[''] focus-visible:ring-[3px] focus-visible:ring-ring/50\"\n\nconst toneFill: Record<Tone, string> = {\n  info: \"bg-info\",\n  success: \"bg-success\",\n  warning: \"bg-warning\",\n  danger: \"bg-danger\",\n  muted: \"bg-muted-foreground\",\n}\nconst toneThumb: Record<Tone, string> = {\n  info: \"bg-info\",\n  success: \"bg-success\",\n  warning: \"bg-warning\",\n  danger: \"bg-danger\",\n  muted: \"bg-muted-foreground\",\n}\n/* The track is a very low-intensity form of the tone (alpha via color-mix only). */\nconst toneTrack: Record<Tone, string> = {\n  info: \"[background-color:color-mix(in_oklab,var(--info)_18%,transparent)]\",\n  success: \"[background-color:color-mix(in_oklab,var(--success)_18%,transparent)]\",\n  warning: \"[background-color:color-mix(in_oklab,var(--warning)_18%,transparent)]\",\n  danger: \"[background-color:color-mix(in_oklab,var(--danger)_18%,transparent)]\",\n  muted: \"bg-secondary\",\n}\n\nexport type ToneSliderProps = {\n  className?: string\n  size?: StyledSize\n  min?: number\n  max?: number\n  step?: number\n  value?: number\n  defaultValue?: number\n  onValueChange?: (v: number) => void\n}\n\nfunction clamp(v: number, min: number, max: number) {\n  return Math.max(min, Math.min(max, v))\n}\n\n/* Shared horizontal slider behaviour: controlled/uncontrolled value, pointer,\n   keyboard. */\nfunction useSlider(props: ToneSliderProps) {\n  const {\n    value,\n    defaultValue = 55,\n    onValueChange,\n    min = 0,\n    max = 100,\n    step = 1,\n  } = props\n  const trackRef = React.useRef<HTMLDivElement>(null)\n  const [internal, setInternal] = React.useState(() => clamp(defaultValue, min, max))\n  const [dragging, setDragging] = React.useState(false)\n  const current = value !== undefined ? clamp(value, min, max) : internal\n\n  const commit = React.useCallback(\n    (next: number) => {\n      const c = clamp(Math.round(next / step) * step, min, max)\n      if (value === undefined) setInternal(c)\n      onValueChange?.(c)\n    },\n    [max, min, onValueChange, step, value]\n  )\n\n  const fromClientX = React.useCallback(\n    (clientX: number) => {\n      const el = trackRef.current\n      if (!el) return current\n      const rect = el.getBoundingClientRect()\n      const ratio = rect.width > 0 ? (clientX - rect.left) / rect.width : 0\n      return min + clamp(ratio, 0, 1) * (max - min)\n    },\n    [current, max, min]\n  )\n\n  const onPointerDown = React.useCallback(\n    (e: React.PointerEvent) => {\n      e.preventDefault()\n      trackRef.current?.setPointerCapture?.(e.pointerId)\n      setDragging(true)\n      commit(fromClientX(e.clientX))\n    },\n    [commit, fromClientX]\n  )\n\n  const onThumbPointerDown = React.useCallback((e: React.PointerEvent) => {\n    e.preventDefault()\n    e.stopPropagation()\n    trackRef.current?.setPointerCapture?.(e.pointerId)\n    setDragging(true)\n  }, [])\n\n  const onPointerMove = React.useCallback(\n    (e: React.PointerEvent) => {\n      if (!dragging) return\n      commit(fromClientX(e.clientX))\n    },\n    [commit, dragging, fromClientX]\n  )\n\n  const onPointerUp = React.useCallback((e: React.PointerEvent) => {\n    trackRef.current?.releasePointerCapture?.(e.pointerId)\n    setDragging(false)\n  }, [])\n\n  const onKeyDown = React.useCallback(\n    (e: React.KeyboardEvent) => {\n      const delta = e.shiftKey ? step * 10 : step\n      let next = current\n      if (e.key === \"ArrowLeft\" || e.key === \"ArrowDown\") next = current - delta\n      else if (e.key === \"ArrowRight\" || e.key === \"ArrowUp\") next = current + delta\n      else if (e.key === \"Home\") next = min\n      else if (e.key === \"End\") next = max\n      else return\n      e.preventDefault()\n      commit(next)\n    },\n    [commit, current, max, min, step]\n  )\n\n  const pct = max > min ? ((current - min) / (max - min)) * 100 : 0\n\n  return {\n    trackRef,\n    current,\n    pct,\n    dragging,\n    min,\n    max,\n    onPointerDown,\n    onThumbPointerDown,\n    onPointerMove,\n    onPointerUp,\n    onKeyDown,\n  }\n}\n\n/* A single body: only the tone classes change. The state lives in this component and\n   is never inside a conditional subtree. */\nfunction SemanticSlider({\n  tone,\n  label,\n  className,\n  size = \"md\",\n  ...rest\n}: ToneSliderProps & { tone: Tone; label: string }) {\n  const reduce = useReducedMotion()\n  const s = useSlider(rest)\n  return (\n    <div\n      data-slot=\"styled-slider\"\n      data-tone={tone}\n      className={cn(rootBase, className)}\n    >\n      <div\n        ref={s.trackRef}\n        onPointerDown={s.onPointerDown}\n        onPointerMove={s.onPointerMove}\n        onPointerUp={s.onPointerUp}\n        className={cn(\n          \"relative w-full cursor-pointer rounded-full\",\n          toneTrack[tone],\n          trackHeight[size]\n        )}\n      >\n        <div\n          className={cn(\"absolute inset-y-0 left-0 rounded-full\", toneFill[tone])}\n          style={{ width: `${s.pct}%` }}\n        />\n        <motion.button\n          type=\"button\"\n          data-slot=\"styled-slider-thumb\"\n          role=\"slider\"\n          aria-label={label}\n          aria-valuenow={Math.round(s.current)}\n          aria-valuemin={s.min}\n          aria-valuemax={s.max}\n          aria-orientation=\"horizontal\"\n          tabIndex={0}\n          onPointerDown={s.onThumbPointerDown}\n          onKeyDown={s.onKeyDown}\n          className={cn(thumbBase, thumbSize[size], toneThumb[tone])}\n          style={{ left: `${s.pct}%` }}\n          animate={{ scale: !reduce && s.dragging ? 1.18 : 1 }}\n          transition={\n            reduce\n              ? { duration: 0 }\n              : { type: \"spring\" as const, stiffness: 500, damping: 30, mass: 0.6 }\n          }\n        />\n      </div>\n    </div>\n  )\n}\n\n/* Info: bilgilendirici mavi ton. */\nexport function InfoSlider(props: ToneSliderProps) {\n  return <SemanticSlider tone=\"info\" label=\"Info value\" {...props} />\n}\n\n/* Success: olumlu yesil ton. */\nexport function SuccessSlider(props: ToneSliderProps) {\n  return <SemanticSlider tone=\"success\" label=\"Success value\" {...props} />\n}\n\n/* Warning: uyari tonu. */\nexport function WarningSlider(props: ToneSliderProps) {\n  return <SemanticSlider tone=\"warning\" label=\"Warning value\" {...props} />\n}\n\n/* Danger: only the ai2 danger token is used. */\nexport function DangerSlider(props: ToneSliderProps) {\n  return <SemanticSlider tone=\"danger\" label=\"Danger value\" {...props} />\n}\n\n/* Muted: notr, dusuk vurgulu ton. */\nexport function MutedSlider(props: ToneSliderProps) {\n  return <SemanticSlider tone=\"muted\" label=\"Value\" {...props} />\n}\n",
      "type": "registry:component",
      "target": "components/ui/slider-tone.tsx"
    }
  ],
  "categories": [
    "styled",
    "slider"
  ],
  "type": "registry:component"
}