crucıble

Hopper

A file-upload dropzone over a faint coordinate grid: dragging a file over it lifts the zone and brightens the grid, and dropped files seat in as stacked cards showing name, size, and type — each removable by pointer or keyboard. Backed by a real hidden file input with type, size, and count validation; the zone opens the native picker on click, Enter, or Space.

motionfree

Installation

CLI

npx shadcn@latest add @crucible/hopper

Manual — install dependencies, then copy the source

npm install motion

Props

PropTypeDefaultDescription
acceptstringundefined — any fileNative accept string, honoured for BOTH the picker and drag-drop (drops bypass the input's own filtering, so it is re-checked here). e.g. "image/*,.pdf".
multiplebooleantrueAllow more than one file. When false, a new file replaces the last.
maxSizenumberundefined — no limitMax bytes per file; larger files are rejected.
maxCountnumberundefined — no limitMax number of files held at once; extras are rejected.
onChange(files: File[]) => voidFires whenever the held set changes, with the native files in order.
onReject(rejections: HopperRejection[]) => voidFires with any files turned away (wrong type / too large / over count).
labelReact.ReactNode"Drop files or click to browse"Headline inside the zone.
hintReact.ReactNodeSub-line under the headline; defaults to a generated accept/size summary.
renderCard(args: { file: File; id: string; index: number; remove: () => void; }) => React.ReactNodePer-file card slot — replaces the default card body (the surrounding focusable <li> with Delete-to-remove is always kept for a11y).
renderProgress(args: { file: File; id: string }) => React.ReactNodeProgress slot rendered inside each default card, e.g. an upload bar.
accentstringskyThe single cool accent for the active drag border/glow.
disabledbooleanfalseDisable the whole control.
pausedbooleanfalseFreeze motion (grid brighten, card spring).
classNamestring

Honors prefers-reduced-motion with a designed static fallback, and passes className through.