

<Preview name="FileDetailsSingleExample" />

## Overview [#overview]

`FileDetails` is a composable compound component for displaying file metadata and actions. It is container-agnostic — place it inside a Sheet, sticky sidebar, Dialog, or bare div. Compose only the pieces you need: icon, name, properties, actions.

## Usage [#usage]

```tsx
import { FileDetails } from "@tilt-legal/cubitt-components/composites";
```

```tsx
<FileDetails
  files={file}
  onRename={handleRename}
  onDownload={handleDownload}
  onArchive={handleArchive}
  onDelete={handleDelete}
>
  <FileDetails.Icon />
  <FileDetails.Name />
  <FileDetails.Separator />
  <FileDetails.Properties>
    <FileDetails.Property label="Size" />
    <FileDetails.Property label="Created by" />
    <FileDetails.Property label="Created" />
  </FileDetails.Properties>
  <FileDetails.Separator />
  <FileDetails.Actions>
    <FileDetails.DownloadAction />
    <FileDetails.RenameAction />
    <FileDetails.ArchiveAction />
    <FileDetails.DeleteAction />
  </FileDetails.Actions>
</FileDetails>
```

Sub-components self-hide when not applicable — `Icon` returns null for multi-file, `MultiIcon` returns null for single-file, action buttons hide when their handler is not provided.

## Examples [#examples]

### Single File [#single-file]

Full details panel with icon, editable name, properties, status badge, and all actions.

<Tabs items="['Preview', 'Code']">
  <Tab value="Preview">
    <Preview className="py-6" name="FileDetailsSingleExample" />
  </Tab>

  <Tab value="Code">
    ```tsx
    import { FileDetails } from "@tilt-legal/cubitt-components/composites";

    export function Example() {
      return (
        <FileDetails
          files={file}
          onRename={handleRename}
          onDownload={handleDownload}
          onArchive={handleArchive}
          onDelete={handleDelete}
        >
          <FileDetails.Icon />
          <FileDetails.Name />
          <FileDetails.Separator />
          <FileDetails.Properties>
            <FileDetails.Property label="Status">
              <FileDetails.Badge />
            </FileDetails.Property>
            <FileDetails.Property label="Size" />
            <FileDetails.Property label="Created by" />
            <FileDetails.Property label="Created" />
            <FileDetails.Property label="Updated" />
            <FileDetails.Property label="File ID" />
          </FileDetails.Properties>
          <FileDetails.Separator />
          <FileDetails.Actions>
            <FileDetails.DownloadAction />
            <FileDetails.RenameAction />
            <FileDetails.ArchiveAction />
            <FileDetails.DeleteAction />
          </FileDetails.Actions>
        </FileDetails>
      );
    }
    ```
  </Tab>
</Tabs>

### Multi-File Selection [#multi-file-selection]

Stacked icon preview with combined stats and batch actions. Use `"Files selected"` and `"Total size"` known labels on `Property` for multi-file metadata.

<Tabs items="['Preview', 'Code']">
  <Tab value="Preview">
    <Preview className="py-6" name="FileDetailsMultiExample" />
  </Tab>

  <Tab value="Code">
    ```tsx
    import { FileDetails } from "@tilt-legal/cubitt-components/composites";

    export function Example() {
      return (
        <FileDetails
          files={selectedFiles}
          onDownload={handleDownload}
          onArchive={handleArchive}
        >
          <FileDetails.MultiIcon />
          <FileDetails.Separator />
          <FileDetails.Properties>
            <FileDetails.Property label="Files selected" />
            <FileDetails.Property label="Total size" />
          </FileDetails.Properties>
          <FileDetails.Separator />
          <FileDetails.Actions>
            <FileDetails.DownloadAction />
            <FileDetails.ArchiveAction />
          </FileDetails.Actions>
        </FileDetails>
      );
    }
    ```
  </Tab>
</Tabs>

### Panels (Tabbed Content) [#panels-tabbed-content]

Wrap content in `FileDetails.Panels` with multiple `FileDetails.Panel` children to enable tab switching. Content above `Panels` (Icon, Name) is shared across all tabs. When only one `Panel` is provided, tabs are not rendered.

<Tabs items="['Preview', 'Code']">
  <Tab value="Preview">
    <Preview className="py-6" name="FileDetailsPanelsExample" />
  </Tab>

  <Tab value="Code">
    ```tsx
    import { FileDetails } from "@tilt-legal/cubitt-components/composites";

    export function Example() {
      return (
        <FileDetails files={file} onRename={handleRename} onDownload={handleDownload}>
          <FileDetails.Icon />
          <FileDetails.Name />
          <FileDetails.Panels>
            <FileDetails.Panel id="details" label="Details">
              <FileDetails.Properties>
                <FileDetails.Property label="Size" />
                <FileDetails.Property label="Created by" />
                <FileDetails.Property label="Created" />
              </FileDetails.Properties>
              <FileDetails.Separator />
              <FileDetails.Actions>
                <FileDetails.DownloadAction />
                <FileDetails.RenameAction />
              </FileDetails.Actions>
            </FileDetails.Panel>
            <FileDetails.Panel id="activity" label="Activity">
              <ActivityFeed fileId={file.id} />
            </FileDetails.Panel>
          </FileDetails.Panels>
        </FileDetails>
      );
    }
    ```
  </Tab>
</Tabs>

### Panels with URL State [#panels-with-url-state]

Pass `paramName` to persist the active tab in a URL query parameter. When used inside `Files.DetailsPanel`, use `paramName="files-panel"` to share the parameter — any non-empty value keeps the panel open and the value identifies the active tab. Switch tabs below and notice the URL updates.

<Tabs items="['Preview', 'Code']">
  <Tab value="Preview">
    <Preview className="py-6" name="FileDetailsPanelsUrlStateExample" />
  </Tab>

  <Tab value="Code">
    ```tsx
    import { FileDetails } from "@tilt-legal/cubitt-components/composites";

    export function Example() {
      return (
        <FileDetails files={file} onRename={handleRename} onDownload={handleDownload}>
          <FileDetails.Icon />
          <FileDetails.Name />
          <FileDetails.Panels paramName="files-panel">
            <FileDetails.Panel id="details" label="Details">
              <FileDetails.Properties>
                <FileDetails.Property label="Size" />
                <FileDetails.Property label="Created by" />
                <FileDetails.Property label="Created" />
              </FileDetails.Properties>
              <FileDetails.Separator />
              <FileDetails.Actions>
                <FileDetails.DownloadAction />
                <FileDetails.RenameAction />
              </FileDetails.Actions>
            </FileDetails.Panel>
            <FileDetails.Panel id="activity" label="Activity">
              <ActivityFeed fileId={file.id} />
            </FileDetails.Panel>
          </FileDetails.Panels>
        </FileDetails>
      );
    }
    ```
  </Tab>
</Tabs>

### Panels with File Type Filtering [#panels-with-file-type-filtering]

Add a `fileTypes` prop to `FileDetails.Panel` to conditionally show panels based on the file type. Panels without `fileTypes` are always visible. When filtering reduces visible panels to one, tabs automatically hide.

The `fileTypes` prop accepts:

* `"standard"` / `"virtual"` — match by asset type
* MIME patterns (contain `/`, e.g. `"audio/*"`) — match standard files by MIME type
* Other strings (e.g. `"transcript"`) — match virtual files by `virtualType` discriminator

<Tabs items="['Preview', 'Code']">
  <Tab value="Preview">
    <Preview className="py-6" name="FileDetailsPanelsMimeTypeExample" />
  </Tab>

  <Tab value="Code">
    ```tsx
    import { FileDetails } from "@tilt-legal/cubitt-components/composites";

    export function Example() {
      return (
        <FileDetails files={file}>
          <FileDetails.Icon />
          <FileDetails.Name />
          <FileDetails.Panels>
            <FileDetails.Panel id="details" label="Details">
              <FileDetails.Properties>
                <FileDetails.Property label="Size" />
                <FileDetails.Property label="Created by" />
                <FileDetails.Property label="Created" />
              </FileDetails.Properties>
            </FileDetails.Panel>
            <FileDetails.Panel
              id="transcript"
              label="Transcript"
              fileTypes={["audio/*", "video/*"]}
            >
              <TranscriptContent fileId={file.id} />
            </FileDetails.Panel>
          </FileDetails.Panels>
        </FileDetails>
      );
    }
    ```
  </Tab>
</Tabs>

### Panels with Virtual Type Filtering [#panels-with-virtual-type-filtering]

Use `fileTypes` with a virtual type discriminator (e.g. `"transcript"`) to show panels only for matching virtual files. You can also pass an array to match multiple types (OR logic).

<Tabs items="['Preview', 'Code']">
  <Tab value="Preview">
    <Preview className="py-6" name="FileDetailsPanelsVirtualTypeExample" />
  </Tab>

  <Tab value="Code">
    ```tsx
    import { FileDetails } from "@tilt-legal/cubitt-components/composites";

    export function Example() {
      return (
        <FileDetails files={file}>
          <FileDetails.Icon />
          <FileDetails.Name />
          <FileDetails.Panels>
            <FileDetails.Panel id="details" label="Details">
              <FileDetails.Properties>
                <FileDetails.Property label="Size" />
                <FileDetails.Property label="Created by" />
                <FileDetails.Property label="Created" />
              </FileDetails.Properties>
            </FileDetails.Panel>
            <FileDetails.Panel
              id="transcript"
              label="Transcript"
              fileTypes="transcript"
            >
              <TranscriptViewer fileId={file.id} />
            </FileDetails.Panel>
          </FileDetails.Panels>
        </FileDetails>
      );
    }
    ```
  </Tab>
</Tabs>

### Per-Type Panel Composition [#per-type-panel-composition]

Use `fileTypes` to define different panels for different file types. When all panels have a `fileTypes` filter and multiple files are selected, `Panels` renders a built-in multi-file summary (files count + total size + actions if all files share the same asset type). Override with the `fallback` prop.

```tsx
<FileDetails.Panels>
  {/* Standard files: full details + all actions */}
  <FileDetails.Panel id="standard-details" label="Details" fileTypes="standard">
    <FileDetails.Properties>
      <FileDetails.Property label="Size" />
      <FileDetails.Property label="Created by" />
      <FileDetails.Property label="Created" />
    </FileDetails.Properties>
    <FileDetails.Separator />
    <FileDetails.Actions>
      <FileDetails.DownloadAction />
      <FileDetails.RenameAction />
      <FileDetails.ArchiveAction />
    </FileDetails.Actions>
  </FileDetails.Panel>

  {/* Transcript virtual files: tailored properties + delete only */}
  <FileDetails.Panel id="transcript-details" label="Details" fileTypes="transcript">
    <FileDetails.Properties>
      <FileDetails.Property label="Created" />
      <FileDetails.Property label="Created by" />
    </FileDetails.Properties>
    <FileDetails.Separator />
    <FileDetails.Actions>
      <FileDetails.DeleteAction />
    </FileDetails.Actions>
  </FileDetails.Panel>
</FileDetails.Panels>
```

### Custom Properties [#custom-properties]

Override the default property set. Use `FileDetails.Property` with known labels for auto-lookup or provide custom values via children.

<Tabs items="['Preview', 'Code']">
  <Tab value="Preview">
    <Preview className="py-6" name="FileDetailsCustomPropertiesExample" />
  </Tab>

  <Tab value="Code">
    ```tsx
    import { FileDetails } from "@tilt-legal/cubitt-components/composites";

    export function Example() {
      return (
        <FileDetails files={file} onDownload={handleDownload}>
          <FileDetails.Icon size={64} />
          <FileDetails.Name />
          <FileDetails.Separator />
          <FileDetails.Properties>
            <FileDetails.Property label="Size" />
            <FileDetails.Property label="Uploaded by" />
            <FileDetails.Property label="Matter">Smith v. Jones</FileDetails.Property>
            <FileDetails.Property label="Category">Correspondence</FileDetails.Property>
            <FileDetails.Property label="File ID" />
          </FileDetails.Properties>
          <FileDetails.Separator />
          <FileDetails.Actions>
            <FileDetails.DownloadAction />
          </FileDetails.Actions>
        </FileDetails>
      );
    }
    ```
  </Tab>
</Tabs>

***

## API Reference [#api-reference]

### FileDetails (Root) [#filedetails-root]

Provider and layout container. Accepts a single file or an array.

| Prop         | Type                                                    | Default      | Description                                                   |
| ------------ | ------------------------------------------------------- | ------------ | ------------------------------------------------------------- |
| `children`   | `ReactNode`                                             | **Required** | Sub-components to render.                                     |
| `files`      | `DisplayFile \| DisplayFile[]`                          | **Required** | File(s) to display details for.                               |
| `onDownload` | `(ids: string[]) => Promise<void>`                      | –            | Download handler. Shows download action when provided.        |
| `onRename`   | `(file: DisplayFile, newName: string) => Promise<void>` | –            | Rename handler. Shows rename affordances when provided.       |
| `onArchive`  | `(ids: string[]) => void`                               | –            | Archive handler. Shows archive action for non-archived files. |
| `onDelete`   | `(ids: string[]) => void`                               | –            | Delete handler. Shows delete action for archived files only.  |
| `className`  | `string`                                                | –            | Additional CSS classes.                                       |

### FileDetails.Icon [#filedetailsicon]

MIME type icon for single-file selection. Self-hides when multi-file or empty.

| Prop        | Type     | Default | Description             |
| ----------- | -------- | ------- | ----------------------- |
| `size`      | `number` | `80`    | Icon size in pixels.    |
| `className` | `string` | –       | Additional CSS classes. |

### FileDetails.MultiIcon [#filedetailsmultiicon]

Stacked icon display for multi-file selection. Shows up to 3 file icons with a `+N` overflow indicator. Self-hides when single-file or empty.

| Prop        | Type     | Default | Description             |
| ----------- | -------- | ------- | ----------------------- |
| `className` | `string` | –       | Additional CSS classes. |

### FileDetails.Name [#filedetailsname]

Editable file name with file type label underneath. Renders an inline textarea when `onRename` is provided (always editable, auto-grows for long names), otherwise a static `h3`. Includes a rename/confirm button and the file type label (e.g. "PDF Document"). Single-file only.

| Prop        | Type      | Default | Description                                                                    |
| ----------- | --------- | ------- | ------------------------------------------------------------------------------ |
| `hideType`  | `boolean` | `false` | Hide the file type label. Useful when rendering `FileDetails.Type` separately. |
| `className` | `string`  | –       | Additional CSS classes.                                                        |

### FileDetails.Type [#filedetailstype]

Standalone file type label (e.g. "PDF Document"). Single-file only. Note: `FileDetails.Name` already includes the type label — use this only if you need it rendered separately.

| Prop        | Type     | Default | Description             |
| ----------- | -------- | ------- | ----------------------- |
| `className` | `string` | –       | Additional CSS classes. |

### FileDetails.Badge [#filedetailsbadge]

Status badge showing the file's current status. Single-file only.

| Prop        | Type                   | Default | Description             |
| ----------- | ---------------------- | ------- | ----------------------- |
| `size`      | `"sm" \| "md" \| "lg"` | `"sm"`  | Badge size.             |
| `className` | `string`               | –       | Additional CSS classes. |

### FileDetails.Properties [#filedetailsproperties]

Container for property rows. Requires `FileDetails.Property` children — renders nothing without them.

| Prop        | Type        | Default      | Description                      |
| ----------- | ----------- | ------------ | -------------------------------- |
| `children`  | `ReactNode` | **Required** | `FileDetails.Property` elements. |
| `className` | `string`    | –            | Additional CSS classes.          |

### FileDetails.Property [#filedetailsproperty]

Single label–value row. Known labels (`"Size"`, `"Created by"`, `"Uploaded by"`, `"Created"`, `"Updated"`, `"File ID"`, `"Files selected"`, `"Total size"`) auto-resolve their values from context. Provide `value` or `children` to override.

| Prop        | Type        | Default      | Description                                |
| ----------- | ----------- | ------------ | ------------------------------------------ |
| `label`     | `string`    | **Required** | Property label.                            |
| `value`     | `ReactNode` | –            | Explicit value (overrides auto-lookup).    |
| `children`  | `ReactNode` | –            | Value as children (overrides auto-lookup). |
| `className` | `string`    | –            | Additional CSS classes.                    |

### FileDetails.Panels [#filedetailspanels]

Container that enables tabbed content switching. When it contains 2+ `Panel` children, renders tabs with `variant="line"` and the default tabs sizing. When only 1 `Panel` child is provided, renders the content directly without tabs. When all panels are filtered out during multi-file selection, renders a built-in summary (files count + total size + actions if same asset type).

| Prop           | Type        | Default            | Description                                                                                                                                                                                                                                                      |
| -------------- | ----------- | ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `children`     | `ReactNode` | **Required**       | `FileDetails.Panel` elements.                                                                                                                                                                                                                                    |
| `defaultValue` | `string`    | First panel's `id` | Default active tab.                                                                                                                                                                                                                                              |
| `paramName`    | `string`    | –                  | URL query parameter name for persisting the active tab. When used inside `Files.DetailsPanel`, pass `"files-panel"` to share the parameter with the details panel open state — any non-empty value keeps the panel open and the value identifies the active tab. |
| `fallback`     | `ReactNode` | –                  | Content to render when no panels match (e.g. multi-file selection). Overrides the built-in multi-file summary.                                                                                                                                                   |
| `className`    | `string`    | –                  | Additional CSS classes.                                                                                                                                                                                                                                          |

### FileDetails.Panel [#filedetailspanel]

A single content panel within `Panels`. Renders as a tab content pane when tabs are active, or as a plain container when it's the only panel.

| Prop        | Type                 | Default      | Description                                                                                                                                                                                                                                                   |
| ----------- | -------------------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`        | `string`             | **Required** | Unique identifier, used as the tab value.                                                                                                                                                                                                                     |
| `label`     | `string`             | **Required** | Tab label text.                                                                                                                                                                                                                                               |
| `fileTypes` | `string \| string[]` | –            | File type filter — panel only renders when the file matches. Accepts `"standard"` / `"virtual"` (asset type), MIME patterns like `"audio/*"` (standard files), or virtual type discriminators like `"transcript"`. When omitted, the panel is always visible. |
| `children`  | `ReactNode`          | **Required** | Panel content.                                                                                                                                                                                                                                                |
| `className` | `string`             | –            | Additional CSS classes.                                                                                                                                                                                                                                       |

### FileDetails.Actions [#filedetailsactions]

Container for action buttons. Requires action children — renders nothing without them.

| Prop        | Type        | Default      | Description                                                     |
| ----------- | ----------- | ------------ | --------------------------------------------------------------- |
| `children`  | `ReactNode` | **Required** | Action button elements (e.g. `DownloadAction`, `RenameAction`). |
| `className` | `string`    | –            | Additional CSS classes.                                         |

### FileDetails.DownloadAction [#filedetailsdownloadaction]

Download button. Self-hides when `onDownload` is not provided.

### FileDetails.RenameAction [#filedetailsrenameaction]

Rename button. Self-hides when `onRename` is not provided or multi-file.

### FileDetails.ArchiveAction [#filedetailsarchiveaction]

Archive button. Self-hides when `onArchive` is not provided or files are already archived.

### FileDetails.DeleteAction [#filedetailsdeleteaction]

Delete button. Self-hides when `onDelete` is not provided or files are not archived.

### FileDetails.Empty [#filedetailsempty]

Renders empty state when no files are provided. Accepts custom children or shows a default empty state.

| Prop        | Type        | Default | Description                 |
| ----------- | ----------- | ------- | --------------------------- |
| `children`  | `ReactNode` | –       | Custom empty state content. |
| `className` | `string`    | –       | Additional CSS classes.     |

### FileDetails.Separator [#filedetailsseparator]

Context-aware separator that hides when empty.

| Prop        | Type     | Default | Description             |
| ----------- | -------- | ------- | ----------------------- |
| `className` | `string` | –       | Additional CSS classes. |

### FileDetails.useContext [#filedetailsusecontext]

Hook to access the `FileDetails` context from any child component. Useful for building custom panel content that needs file metadata. Returns `FileDetailsContextValue` with three namespaces:

| Namespace | Key Fields                                                                          | Description                                                     |
| --------- | ----------------------------------------------------------------------------------- | --------------------------------------------------------------- |
| `meta`    | `file`, `files`, `fileIds`, `isSingleFile`, `isMultiFile`, `isEmpty`                | File metadata. `file` is the single selected file (or `null`).  |
| `meta`    | `canDownload`, `canRename`, `canArchive`, `canDelete`, `isDirty`                    | Capability flags derived from provided handlers and file state. |
| `state`   | `renameValue`, `renamePending`, `isDownloading`                                     | Current UI state.                                               |
| `actions` | `download`, `archive`, `requestDelete`, `focusName`, `submitRename`, `cancelRename` | Action dispatchers bound to the provided handlers.              |
