# BreadcrumbTree

A breadcrumb-style picker that opens a Finder-style Miller-column panel,
anchored to the breadcrumb. The panel floats above any clipping ancestor.
Children are fetched on demand with pagination, results are reused when you
revisit a node, and the columns auto-scroll to the deepest level.

## Demo

Click the chevron to open the panel. Drill into nodes by clicking rows. Click a
breadcrumb item to navigate back (the panel closes).

## rootPath and fetchChildren

When `rootPath` is provided (e.g. `"my-project"`), the component prepends it to
every `fetchChildren` call:

| Column depth | `path` argument to `fetchChildren` |
| --- | --- |
| Root | `"my-project"` |
| After selecting "datasets" | `"my-project/datasets"` |
| After selecting "droid-2024" | `"my-project/datasets/droid-2024"` |

Without `rootPath`, the root call uses `""` (empty string) and sub-calls use
`"datasets"`, `"datasets/droid-2024"`, etc.

Results are reused when you drill into the same node twice, so revisiting a
folder does not trigger a second request.

## Pagination

Each column loads its first page on open. Scrolling to the bottom of a column
loads the next page automatically.

## Floating panel

The panel floats above the content, so it is not clipped by `overflow: hidden`,
`transform`, or sticky containers. It tracks the trigger as the page scrolls or
resizes.

> **Controlled path** — `BreadcrumbTree` is fully controlled. Row clicks call
> `onNavigate(node, newPath)`; the caller updates `path`. The panel stays open
> after a row click and closes when a breadcrumb item is clicked.

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `path` | `BreadcrumbNode[]` | — | Controlled path from root to the selected node. Empty array = root. |
| `onNavigate` | `(node, newPath) => void` | — | Fires on row click (panel stays open) and breadcrumb click (panel closes). Update `path` here. |
| `fetchChildren` | `(path, page, limit) => Promise` | — | Async loader. `path` is slash-separated. Prepended with `rootPath` when set. |
| `rootPath` | `string` | — | Root prefix shown as the first breadcrumb item. |
| `renderEmpty` | `(parentNode) => ReactNode` | `"No items"` | Custom empty-state per column. Receives the parent node (`null` at root). |
| `refreshKey` | `number` | `0` | Increment to re-fetch the deepest visible column. |
| `refreshToken` | `number` | `0` | Increment to drop the whole cache after a host mutation (delete/rename). Open columns re-fetch immediately; otherwise they re-fetch fresh on next open. |
| `placeholder` | `string` | `"Select folder"` | Shown when `path` is empty and `rootPath` is unset. |
| `className` | `string` | — | Extra classes on the wrapper. |

### Types

```ts
interface BreadcrumbNode {
  id: string
  name: string
  /** When false, hides the chevron — signals no sub-children exist. */
  hasChildren?: boolean
}

interface FetchChildrenResult {
  items: BreadcrumbNode[]
  totalPages: number
}

// Per-column state, also exported for callers that inspect column status.
interface ColumnData {
  items: BreadcrumbNode[]
  page: number
  totalPages: number
  hasMore: boolean
  loading: boolean
  error: Error | null
}
```
