# Crate — Animated App UI Block for React

> 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.

- Page: https://crucibleui.com/components/app/crate
- Category: [app](https://crucibleui.com/components/app)
- Tier: pro (one-time purchase, see https://crucibleui.com/pricing)
- Engine: motion
- npm dependencies: motion

## Install

```bash
npx shadcn@latest add @crucible/crate
```

Pro install requires adding the token-bearing `@crucible` registry to components.json (issued at https://crucibleui.com/account after purchase).

Import from `@/components/crucible/crate`.

## Props (`CrateProps`)

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `tree?` | `CrateNode` | — | Controlled tree — the root folder whose `children` are the top level. Pair with `onTreeChange`; edits you don't feed back in are not kept. |
| `defaultTree?` | `CrateNode` | `a sample workspace (projects, design, photos…)` | Initial tree for uncontrolled use. |
| `onTreeChange?` | `(tree: CrateNode, change: CrateChange) => void` | — | Fires after every edit (rename, move, delete, duplicate, new folder, upload) with the full next tree. |
| `view?` | `CrateView` | — | Controlled view mode. |
| `defaultView?` | `CrateView` | `"grid"` | Initial view mode for uncontrolled use. |
| `onViewChange?` | `(view: CrateView) => void` | — | Fires when the user flips between grid and list. |
| `defaultFolder?` | `string` | `the root` | Id of the folder to start in. |
| `defaultPreviewOpen?` | `boolean` | `true` | Whether the details pane starts open (it becomes an overlay below ~720px). |
| `onOpen?` | `(node: CrateNode, path: CrateNode[]) => void` | — | Fires 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[]) => void` | — | Download action for the selection. Omit to show a "Download started" notice only. |
| `onSelectionChange?` | `(ids: string[]) => void` | — | Fires whenever the selection changes. |
| `accentColor?` | `string` | `"#fbbf24"` | The single accent: folder glyphs, selection, marquee, drop targets, focus rings, upload rings and the primary button. Any hex color. |
| `label?` | `string` | `"File manager"` | Accessible name of the file browser. |
| `ref?` | `React.Ref<CrateHandle>` | — | Imperative handle — `select()`, `clearSelection()`, `navigate()`, `replay()`, `focus()`. |
| `className?` | `string` | — | — |

## Usage example

```tsx
"use client";

import { Crate } from "@/components/crucible/crate";

export default function CrateDemo() {
  return (
    <div className="relative isolate flex h-[620px] w-full flex-col gap-3 overflow-hidden rounded-xl bg-neutral-950 p-3 sm:p-5">
      <p className="relative z-10 text-center text-xs text-white/45">Drag to select — double-click a folder.</p>
      <div className="relative z-10 mx-auto flex min-h-0 w-full max-w-5xl flex-1">
        <Crate className="min-h-0" />
      </div>
    </div>
  );
}
```

## Accessibility & performance

Honors `prefers-reduced-motion` with a designed static frame or fade (never a blank box) and passes `className` through.
