Scrub Input
componentry/scrub-inputFreeComponent
An inline slider input styled as a pill, perfect for adjusting variables smoothly.
Live preview
live · rendered by the registry
Rendered by componentry on their own page — this is the component running, not a screenshot of it.Install it
npx shadcn@latest add https://componentry.dev/r/scrub-input.jsonSource
1"use client";23import React, { useRef, useState } from "react";4import { cn } from "@workspace/ui/lib/utils";56export interface ScrubInputProps {7 value?: number;8 defaultValue?: number;9 onChange?: (value: number) => void;10 label: string;11 min?: number;12 max?: number;13 step?: number;14 className?: string;15}1617export function ScrubInput({18 value: controlledValue,19 defaultValue = 0,20 onChange,21 label,22 min = 0,23 max = 100,24 step = 1,25 className,26}: ScrubInputProps) {27 const [uncontrolledValue, setUncontrolledValue] = useState(defaultValue);28 const isControlled = controlledValue !== undefined;29 const value = isControlled ? controlledValue : uncontrolledValue;3031 const [isDragging, setIsDragging] = useState(false);32 const containerRef = useRef<HTMLDivElement>(null);3334 const handleValueChange = (newValue: number) => {35 if (!isControlled) setUncontrolledValue(newValue);36 onChange?.(newValue);37 };3839 const updateValueFromPointer = (clientX: number) => {40 if (!containerRef.current) return;41 const rect = containerRef.current.getBoundingClientRect();42 const x = clientX - rect.left;43 let percentage = x / rect.width;44 percentage = Math.max(0, Math.min(1, percentage));4546 let newValue = min + percentage * (max - min);47 newValue = Math.round(newValue / step) * step;4849 // Ensure value stays within bounds due to rounding50 newValue = Math.max(min, Math.min(max, newValue));5152 if (newValue !== value) {53 handleValueChange(newValue);54 }55 };5657 const onPointerDown = (e: React.PointerEvent) => {58 containerRef.current?.setPointerCapture(e.pointerId);59 setIsDragging(true);60 updateValueFromPointer(e.clientX);61 };6263 const onPointerMove = (e: React.PointerEvent) => {64 if (!isDragging) return;65 updateValueFromPointer(e.clientX);66 };6768 const onPointerUp = (e: React.PointerEvent) => {69 containerRef.current?.releasePointerCapture(e.pointerId);70 setIsDragging(false);71 };7273 const percentage = max > min ? ((value - min) / (max - min)) * 100 : 0;7475 // Create a minimum width so the fill doesn't look too weird when value is 076 const fillWidth = Math.max(20, percentage);7778 return (79 <div80 ref={containerRef}81 className={cn(82// … truncated
More from componentry
All 59 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| Animated Gradientanimated-gradient | componentry | Components | Free | no deps | |
| ASCII Effectascii-effect | componentry | Components | Free | no deps | |
| Aurora Flowaurora-flow | componentry | Components | Free | no deps | |
| auth-modalauth-modal | componentry | Components | Free | no deps | |
| border-beamborder-beam | componentry | Components | Free | no deps | |
| circuit-boardcircuit-board | componentry | Components | Free | no deps | |
| closing-plasmaclosing-plasma | componentry | Components | Free | no deps | |
| collection-surfercollection-surfer | componentry | Components | Free | no deps |
