# Docket — Animated App UI Block for React

> Full-featured client-side data table block on a real <table>: debounced search with highlighted matches, per-column filter checklists, column visibility and density, tri-state sorting where rows FLIP to their new positions, shift-click range selection with a spring-up bulk action bar (rolling count, confirm-to-delete), expandable detail rows, sticky header and first column, rolling pagination, an empty state, and roving arrow-key focus with aria-sort and live announcements.

- Page: https://crucibleui.com/components/app/docket
- 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/docket
```

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

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

## Props (`DocketProps`)

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `columns?` | `DocketColumn<R>[]` | `sample deployment columns` | Column definitions, in display order. The first column is sticky. |
| `rows?` | `R[]` | `120 seeded sample deployments` | Data rows (client-side; tuned for ~50–500). |
| `getRowId?` | `(row: R) => string` | `row => String(row.id)` | Stable row id. |
| `selectedIds?` | `string[]` | — | Controlled selection (row ids). Pair with `onSelectionChange`. |
| `onSelectionChange?` | `(ids: string[], rows: R[]) => void` | — | Fires whenever the selection changes. |
| `bulkActions?` | `DocketBulkAction<R>[]` | `Export / Archive / Delete` | Actions in the bulk bar that springs up once rows are selected. |
| `onBulkAction?` | `(actionId: string, ids: string[], rows: R[]) => void` | — | Fires after any bulk action resolves. |
| `renderDetail?` | `((row: R) => React.ReactNode) \| false` | `a field list (sample data: deploy detail)` | Expanded-row detail panel. `false` disables expanding. |
| `pageSize?` | `number` | `25` | Initial rows per page. |
| `pageSizeOptions?` | `number[]` | `[10, 25, 50, 100]` | Choices in the page-size select. |
| `density?` | `"compact" \| "comfortable"` | `"comfortable"` | Initial row density. |
| `maxHeight?` | `number` | `440` | Max height of the scrolling table body (header stays sticky). |
| `noun?` | `[string, string]` | `["deployment", "deployments"]` | Singular / plural noun used in copy ("3 deployments selected"). |
| `now?` | `number \| Date` | `the current time, read after mount` | Reference time for relative dates. |
| `accentColor?` | `string` | `"#67e8f9"` | Signature accent: sort arrows, selection, focus rings, progress, highlights. |
| `label?` | `string` | `"Deployments"` | Accessible table caption. |
| `ref?` | `React.Ref<DocketHandle>` | — | Imperative handle — `replay()`, `clearSelection()`. |
| `className?` | `string` | — | — |

## Usage example

```tsx
"use client";

import * as React from "react";

import { Docket } from "@/components/crucible/docket";

export default function DocketDemo() {
  return (
    <div className="relative isolate flex h-full min-h-[600px] w-full items-start justify-center overflow-hidden rounded-xl bg-neutral-950">
      <div
        aria-hidden
        className="pointer-events-none absolute inset-0 -z-10"
        style={{
          background: "radial-gradient(70% 55% at 50% 0%, rgba(103,232,249,0.06), transparent 70%)",
        }}
      />
      <div className="relative z-10 flex w-full max-w-5xl flex-col gap-3 px-3 py-6 sm:px-6">
        <p className="text-xs text-neutral-500">Sort a column — select rows for bulk actions.</p>
        <Docket maxHeight={420} />
      </div>
    </div>
  );
}
```

## Accessibility & performance

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