{
  "name": "slider",
  "dependencies": [],
  "registryDependencies": [],
  "files": [
    {
      "path": "slider.tsx",
      "content": "\"use client\";\r\n\r\nimport { css, themeVars as theme } from \"@yugnex/core\";\r\nimport { useControllableState } from \"@yugnex/core/client\";\r\nimport { forwardRef, useId, type InputHTMLAttributes, type ReactNode } from \"react\";\r\n\r\nconst wrapClass = css({\r\n  display: \"flex\",\r\n  flexDirection: \"column\",\r\n  gap: theme.space[2],\r\n  width: \"100%\",\r\n});\r\n\r\nconst headerClass = css({\r\n  display: \"flex\",\r\n  alignItems: \"baseline\",\r\n  justifyContent: \"space-between\",\r\n  gap: theme.space[2],\r\n});\r\n\r\nconst labelClass = css({\r\n  fontFamily: theme.fontFamily.sans,\r\n  fontSize: theme.fontSize.sm,\r\n  fontWeight: theme.fontWeight.medium,\r\n  color: theme.color.foreground,\r\n});\r\n\r\nconst valueClass = css({\r\n  fontFamily: theme.fontFamily.mono,\r\n  fontVariantNumeric: \"tabular-nums\",\r\n  fontSize: theme.fontSize.xs,\r\n  color: theme.color.mutedForeground,\r\n});\r\n\r\n// A native range input, restyled. Keeping the native element means keyboard\r\n// support, touch handling, and form participation come for free and behave\r\n// exactly as the platform intends — a hand-rolled div slider would have to\r\n// re-implement all of it, usually worse.\r\nconst inputClass = css({\r\n  appearance: \"none\",\r\n  WebkitAppearance: \"none\",\r\n  width: \"100%\",\r\n  height: \"1.25rem\",\r\n  background: \"transparent\",\r\n  cursor: \"pointer\",\r\n  margin: 0,\r\n  \"&:disabled\": { opacity: 0.5, cursor: \"not-allowed\" },\r\n\r\n  \"&::-webkit-slider-runnable-track\": {\r\n    height: \"6px\",\r\n    borderRadius: \"9999px\",\r\n    background: `linear-gradient(to right, ${theme.color.primary} var(--nx-slider-fill, 0%), ${theme.color.muted} var(--nx-slider-fill, 0%))`,\r\n  },\r\n  \"&::-moz-range-track\": {\r\n    height: \"6px\",\r\n    borderRadius: \"9999px\",\r\n    backgroundColor: theme.color.muted,\r\n  },\r\n  \"&::-moz-range-progress\": {\r\n    height: \"6px\",\r\n    borderRadius: \"9999px\",\r\n    backgroundColor: theme.color.primary,\r\n  },\r\n\r\n  \"&::-webkit-slider-thumb\": {\r\n    WebkitAppearance: \"none\",\r\n    appearance: \"none\",\r\n    width: \"1rem\",\r\n    height: \"1rem\",\r\n    marginTop: \"-5px\",\r\n    borderRadius: \"9999px\",\r\n    backgroundColor: theme.color.background,\r\n    border: `2px solid ${theme.color.primary}`,\r\n    boxShadow: theme.shadow.sm,\r\n    transitionProperty: \"transform, box-shadow\",\r\n    transitionDuration: theme.duration.fast,\r\n  },\r\n  \"&:active::-webkit-slider-thumb\": { transform: \"scale(1.15)\" },\r\n  \"&::-moz-range-thumb\": {\r\n    width: \"1rem\",\r\n    height: \"1rem\",\r\n    borderRadius: \"9999px\",\r\n    backgroundColor: theme.color.background,\r\n    border: `2px solid ${theme.color.primary}`,\r\n    boxShadow: theme.shadow.sm,\r\n  },\r\n\r\n  \"&:focus-visible\": { outline: \"none\" },\r\n  \"&:focus-visible::-webkit-slider-thumb\": {\r\n    boxShadow: `0 0 0 3px ${theme.color.accent}`,\r\n  },\r\n  \"&:focus-visible::-moz-range-thumb\": {\r\n    boxShadow: `0 0 0 3px ${theme.color.accent}`,\r\n  },\r\n});\r\n\r\nexport interface SliderProps extends Omit<InputHTMLAttributes<HTMLInputElement>, \"value\" | \"onChange\" | \"type\"> {\r\n  value?: number;\r\n  defaultValue?: number;\r\n  onValueChange?: (value: number) => void;\r\n  min?: number;\r\n  max?: number;\r\n  step?: number;\r\n  label?: ReactNode;\r\n  /** Show the current value next to the label. Pass a function to format it. */\r\n  showValue?: boolean | ((value: number) => ReactNode);\r\n}\r\n\r\n/** A range slider built on the native input, with the track filled up to the current value. */\r\nexport const Slider = forwardRef<HTMLInputElement, SliderProps>(function Slider(\r\n  {\r\n    value,\r\n    defaultValue = 50,\r\n    onValueChange,\r\n    min = 0,\r\n    max = 100,\r\n    step = 1,\r\n    label,\r\n    showValue = true,\r\n    disabled,\r\n    id,\r\n    className,\r\n    style,\r\n    ...props\r\n  },\r\n  ref,\r\n) {\r\n  const [current, setCurrent] = useControllableState({\r\n    value,\r\n    defaultValue,\r\n    onChange: onValueChange,\r\n  });\r\n\r\n  const generatedId = useId();\r\n  const fieldId = id ?? generatedId;\r\n  const range = max - min || 1;\r\n  const percent = Math.min(Math.max(((current - min) / range) * 100, 0), 100);\r\n\r\n  const rendered =\r\n    typeof showValue === \"function\" ? showValue(current) : showValue ? current : null;\r\n\r\n  return (\r\n    <div className={wrapClass}>\r\n      {label || rendered != null ? (\r\n        <div className={headerClass}>\r\n          {label ? (\r\n            <label className={labelClass} htmlFor={fieldId}>\r\n              {label}\r\n            </label>\r\n          ) : (\r\n            <span />\r\n          )}\r\n          {rendered != null ? <span className={valueClass}>{rendered}</span> : null}\r\n        </div>\r\n      ) : null}\r\n      <input\r\n        ref={ref}\r\n        id={fieldId}\r\n        type=\"range\"\r\n        className={className ? `${inputClass} ${className}` : inputClass}\r\n        min={min}\r\n        max={max}\r\n        step={step}\r\n        value={current}\r\n        disabled={disabled}\r\n        onChange={(event) => setCurrent(Number(event.target.value))}\r\n        // Drives the WebKit track gradient; Firefox uses ::-moz-range-progress instead.\r\n        style={{ [\"--nx-slider-fill\" as string]: `${percent}%`, ...style }}\r\n        {...props}\r\n      />\r\n    </div>\r\n  );\r\n});\r\n",
      "type": "registry:component"
    }
  ]
}