crucıble

Crate

File manager pane: collapsing breadcrumb path, grid/list views that FLIP between layouts, sort menu, subtree search with highlighted matches, drag-box marquee + ⌘/Shift multi-select with a spring-up Move / Download / Delete bar, drag-to-folder moves, inline rename and New folder, a docked details pane, right-click / Shift+F10 context menu, and drop-anywhere uploads with per-file progress rings via async onUpload. Folders open with a direction-aware slide; the grid is a roving-focus role=grid.

motionpro

Installation

CLI

npx shadcn@latest add @crucible/crate

Manual — install dependencies, then copy the source

npm install motion

Props

PropTypeDefaultDescription
treeCrateNodeControlled tree — the root folder whose children are the top level. Pair with onTreeChange; edits you don't feed back in are not kept.
defaultTreeCrateNodea sample workspace (projects, design, photos…)Initial tree for uncontrolled use.
onTreeChange(tree: CrateNode, change: CrateChange) => voidFires after every edit (rename, move, delete, duplicate, new folder, upload) with the full next tree.
viewCrateViewControlled view mode.
defaultViewCrateView"grid"Initial view mode for uncontrolled use.
onViewChange(view: CrateView) => voidFires when the user flips between grid and list.
defaultFolderstringthe rootId of the folder to start in.
defaultPreviewOpenbooleantrueWhether the details pane starts open (it becomes an overlay below ~720px).
onOpen(node: CrateNode, path: CrateNode[]) => voidFires when a node is opened (double-click, Enter, or the Open action). Folders also navigate.
onMove(nodes: CrateNode[], target: CrateNode) => void | Promise<unknown>Items were moved into target. Return a Promise to confirm; a rejection reverts the move.
onDelete(nodes: CrateNode[]) => void | Promise<unknown>Items were deleted (after the confirm dialog). A rejected Promise restores them.
onRename(node: CrateNode, name: string) => void | Promise<unknown>A node was renamed. A rejected Promise restores the previous name.
onCreateFolder(folder: CrateNode, parent: CrateNode) => void | Promise<unknown>A folder was created via "New folder" (fires before the inline rename).
onUpload( file: File, onProgress: (fraction: number) => void, folder: CrateNode ) => Promise<void | Partial<Omit<CrateNode, "id" | "children">>>Upload one dropped/picked file. Report progress (0–1) through onProgress; resolve to finish (optionally with node fields to merge, e.g. a server thumbnail URL) or reject to mark it failed. Omit for a simulated upload.
onDownload(nodes: CrateNode[]) => voidDownload action for the selection. Omit to show a "Download started" notice only.
onSelectionChange(ids: string[]) => voidFires whenever the selection changes.
accentColorstring"#fbbf24"The single accent: folder glyphs, selection, marquee, drop targets, focus rings, upload rings and the primary button. Any hex color.
labelstring"File manager"Accessible name of the file browser.
refReact.Ref<CrateHandle>Imperative handle — select(), clearSelection(), navigate(), replay(), focus().
classNamestring

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