Scrub Number
moco/scrubFreeComponent
Tangle-style inline number scrubber: drag the variable itself, with a compact range input for pointer and keyboard access.
Live preview
live · rendered by the registry
Rendered by moco on their own page — this is the component running, not a screenshot of it.Install it
npx shadcn@latest add https://mocoui.site/r/scrub.jsonSource
1"use client";23import * as React from "react";4import "./scrub.css";56export type ScrubNumberProps = {7 value: number;8 onChange: (n: number) => void;9 min: number;10 max: number;11 step?: number;12 /** Accessible label for the range input. */13 label: string;14 /** Formats the displayed value; defaults to String(value). */15 format?: (n: number) => string;16 /** The letter or word that doubles as the drag handle. */17 handle: string;18};1920/**21 * A Tangle-style inline control: the variable itself is a drag handle, and a22 * compact range input sits inline with the text for pointer and keyboard use.23 */24export function ScrubNumber({25 value,26 onChange,27 min,28 max,29 step = 1,30 label,31 format,32 handle,33}: ScrubNumberProps) {34 const id = React.useId();35 const drag = React.useRef<{ x: number; v: number } | null>(null);36 const text = format ? format(value) : String(value);3738 const clamp = (n: number) => Math.min(max, Math.max(min, Math.round(n / step) * step));3940 const onPointerDown = (e: React.PointerEvent) => {41 (e.target as HTMLElement).setPointerCapture(e.pointerId);42 drag.current = { x: e.clientX, v: value };43 };44 const onPointerMove = (e: React.PointerEvent) => {45 if (!drag.current) return;46 const perPx = (max - min) / 320;47 onChange(clamp(drag.current.v + (e.clientX - drag.current.x) * perPx));48 };49 const onPointerUp = () => {50 drag.current = null;51 };5253 return (54 <span className="moco-scrub">55 <span56 className="moco-scrub-handle"57 onPointerDown={onPointerDown}58 onPointerMove={onPointerMove}59 onPointerUp={onPointerUp}60 onPointerCancel={onPointerUp}61 aria-hidden="true"62 >63 {handle}64 </span>65 <label className="moco-sr-only" htmlFor={id}>66 {label}67 </label>68 <input69 id={id}70 className="moco-scrub-mini"71 type="range"72 min={min}73 max={max}74 step={step}75 value={value}76 aria-valuetext={`${label}: ${text}`}77 onChange={(e) => onChange(Number(e.target.value))}78 />79 <span className="moco-num">{text}</span>80 </span>81 );82}
What it pulls in
Other registry items
Files it writes
More from moco
All 20 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| Codecode | moco | Components | Free | 1 dep · 3 files | |
| Comparecompare | moco | Components | Free | no deps · 2 files | |
| Count Upcountup | moco | Components | Free | no deps · 2 files | |
| Covercover | moco | Components | Free | no deps · 2 files | |
| Diagram Kitdiagram | moco | Components | Free | no deps · 2 files | |
| Dot Griddotgrid | moco | Components | Free | no deps · 2 files | |
| Figurefigure | moco | Components | Free | no deps · 2 files | |
| Hookshooks | moco | Components | Free | no deps |
