{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "data-table5",
  "title": "Data table",
  "description": "Data table built with shadcn/ui.",
  "files": [
    {
      "path": "contents/components/data-table/5/index.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport type { CSSProperties } from \"react\";\nimport {\n  flexRender,\n  getCoreRowModel,\n  getFilteredRowModel,\n  getPaginationRowModel,\n  getSortedRowModel,\n  useReactTable,\n  type Cell,\n  type ColumnDef,\n  type ColumnFiltersState,\n  type Header,\n  type SortingState,\n  type VisibilityState\n} from \"@tanstack/react-table\";\nimport {\n  ChevronDown,\n  ColumnsIcon,\n  MoreHorizontal,\n  SearchIcon,\n  Trash2,\n  Download,\n  Copy,\n  FileText,\n  GripVertical,\n  ArrowUpDown\n} from \"lucide-react\";\nimport {\n  DndContext,\n  closestCenter,\n  KeyboardSensor,\n  MouseSensor,\n  TouchSensor,\n  useSensor,\n  useSensors,\n  type DragEndEvent\n} from \"@dnd-kit/core\";\nimport { restrictToHorizontalAxis } from \"@dnd-kit/modifiers\";\nimport {\n  arrayMove,\n  SortableContext,\n  useSortable,\n  horizontalListSortingStrategy\n} from \"@dnd-kit/sortable\";\nimport { CSS } from \"@dnd-kit/utilities\";\n\nimport { Button } from \"@/components/ui/button\";\nimport { Checkbox } from \"@/components/ui/checkbox\";\nimport {\n  DropdownMenu,\n  DropdownMenuCheckboxItem,\n  DropdownMenuContent,\n  DropdownMenuItem,\n  DropdownMenuLabel,\n  DropdownMenuSeparator,\n  DropdownMenuTrigger\n} from \"@/components/ui/dropdown-menu\";\nimport {\n  Table,\n  TableBody,\n  TableCell,\n  TableHead,\n  TableHeader,\n  TableRow\n} from \"@/components/ui/table\";\nimport { InputGroup, InputGroupInput, InputGroupAddon } from \"@/components/ui/input-group\";\n\nconst initialData: Payment[] = [\n  {\n    id: \"m5gr84i9\",\n    amount: 316,\n    status: \"success\",\n    email: \"ken99@example.com\"\n  },\n  {\n    id: \"3u1reuv4\",\n    amount: 242,\n    status: \"success\",\n    email: \"Abe45@example.com\"\n  },\n  {\n    id: \"derv1ws0\",\n    amount: 837,\n    status: \"processing\",\n    email: \"Monserrat44@example.com\"\n  },\n  {\n    id: \"5kma53ae\",\n    amount: 874,\n    status: \"success\",\n    email: \"Silas22@example.com\"\n  },\n  {\n    id: \"bhqecj4p\",\n    amount: 721,\n    status: \"failed\",\n    email: \"carmella@example.com\"\n  }\n];\n\nexport type Payment = {\n  id: string;\n  amount: number;\n  status: \"pending\" | \"processing\" | \"success\" | \"failed\";\n  email: string;\n};\n\nfunction DraggableHeader({ header }: { header: Header<Payment, unknown> }) {\n  const isSelectColumn = header.column.id === \"select\";\n  const isActionsColumn = header.column.id === \"actions\";\n  const isNonDraggable = isSelectColumn || isActionsColumn;\n  const { attributes, isDragging, listeners, setNodeRef, transform, transition } = useSortable({\n    id: header.column.id,\n    disabled: isNonDraggable\n  });\n\n  const style: CSSProperties = {\n    opacity: isDragging ? 0.8 : 1,\n    position: \"relative\",\n    transform: CSS.Translate.toString(transform),\n    transition,\n    whiteSpace: \"nowrap\",\n    width: header.column.getSize(),\n    zIndex: isDragging ? 1 : 0\n  };\n\n  if (isNonDraggable) {\n    return (\n      <TableHead\n        className=\"relative h-10 border-t\"\n        style={{ width: header.column.getSize() }}\n        colSpan={header.colSpan}>\n        <div className={isSelectColumn ? \"flex items-center\" : \"\"}>\n          {header.isPlaceholder\n            ? null\n            : flexRender(header.column.columnDef.header, header.getContext())}\n        </div>\n      </TableHead>\n    );\n  }\n\n  return (\n    <TableHead\n      ref={setNodeRef}\n      className=\"relative h-10 border-t\"\n      style={style}\n      colSpan={header.colSpan}>\n      <div className=\"flex items-center justify-start gap-0.5\">\n        <Button\n          size=\"icon\"\n          variant=\"ghost\"\n          className=\"h-7 w-7\"\n          {...attributes}\n          {...listeners}\n          aria-label=\"Drag to reorder\">\n          <GripVertical className=\"h-3 w-3 opacity-60\" aria-hidden=\"true\" />\n        </Button>\n        {header.column.getCanSort() && (\n          <Button\n            size=\"icon\"\n            variant=\"ghost\"\n            className=\"group h-7 w-7\"\n            onClick={header.column.getToggleSortingHandler()}\n            aria-label=\"Toggle sorting\">\n            <ArrowUpDown className=\"h-4 w-4 shrink-0 opacity-60\" aria-hidden=\"true\" />\n          </Button>\n        )}\n        <span className=\"ms-1 grow truncate\">\n          {header.isPlaceholder\n            ? null\n            : flexRender(header.column.columnDef.header, header.getContext())}\n        </span>\n      </div>\n    </TableHead>\n  );\n}\n\nfunction DragAlongCell({ cell }: { cell: Cell<Payment, unknown> }) {\n  const isSelectColumn = cell.column.id === \"select\";\n  const isActionsColumn = cell.column.id === \"actions\";\n  const isNonDraggable = isSelectColumn || isActionsColumn;\n  const { isDragging, setNodeRef, transform, transition } = useSortable({\n    id: cell.column.id,\n    disabled: isNonDraggable\n  });\n\n  const style: CSSProperties = {\n    opacity: isDragging ? 0.8 : 1,\n    position: \"relative\",\n    transform: CSS.Translate.toString(transform),\n    transition,\n    width: cell.column.getSize(),\n    zIndex: isDragging ? 1 : 0\n  };\n\n  if (isNonDraggable) {\n    return (\n      <TableCell\n        className={isSelectColumn ? \"w-[50px]\" : \"\"}\n        style={{ width: cell.column.getSize() }}>\n        <div className={isSelectColumn ? \"flex items-center\" : \"\"}>\n          {flexRender(cell.column.columnDef.cell, cell.getContext())}\n        </div>\n      </TableCell>\n    );\n  }\n\n  return (\n    <TableCell ref={setNodeRef} className=\"truncate\" style={style}>\n      {flexRender(cell.column.columnDef.cell, cell.getContext())}\n    </TableCell>\n  );\n}\n\nconst baseColumns: ColumnDef<Payment>[] = [\n  {\n    id: \"select\",\n    header: ({ table }) => (\n      <Checkbox\n        checked={\n          table.getIsAllPageRowsSelected() || (table.getIsSomePageRowsSelected() && \"indeterminate\")\n        }\n        onCheckedChange={(value) => table.toggleAllPageRowsSelected(!!value)}\n        aria-label=\"Select all\"\n      />\n    ),\n    cell: ({ row }) => (\n      <Checkbox\n        checked={row.getIsSelected()}\n        onCheckedChange={(value) => row.toggleSelected(!!value)}\n        aria-label=\"Select row\"\n      />\n    ),\n    enableSorting: false,\n    enableHiding: false,\n    size: 50,\n    minSize: 50,\n    maxSize: 50\n  },\n  {\n    accessorKey: \"status\",\n    header: \"Status\",\n    cell: ({ row }) => <div className=\"capitalize\">{row.getValue(\"status\")}</div>\n  },\n  {\n    accessorKey: \"email\",\n    header: \"Email\",\n    cell: ({ row }) => <div className=\"lowercase\">{row.getValue(\"email\")}</div>\n  },\n  {\n    accessorKey: \"amount\",\n    header: () => <div>Amount</div>,\n    cell: ({ row }) => {\n      const amount = parseFloat(row.getValue(\"amount\"));\n\n      const formatted = new Intl.NumberFormat(\"en-US\", {\n        style: \"currency\",\n        currency: \"USD\"\n      }).format(amount);\n\n      return <div>{formatted}</div>;\n    }\n  },\n  {\n    id: \"actions\",\n    enableHiding: false,\n    header: () => <div className=\"text-end\">Actions</div>,\n    cell: ({ row }) => {\n      const payment = row.original;\n\n      return (\n        <div className=\"text-end\">\n          <DropdownMenu>\n            <DropdownMenuTrigger asChild>\n              <Button variant=\"ghost\" className=\"h-8 w-8 p-0\">\n                <span className=\"sr-only\">Open menu</span>\n                <MoreHorizontal />\n              </Button>\n            </DropdownMenuTrigger>\n            <DropdownMenuContent align=\"end\">\n              <DropdownMenuLabel>Actions</DropdownMenuLabel>\n              <DropdownMenuItem onClick={() => navigator.clipboard.writeText(payment.id)}>\n                Copy payment ID\n              </DropdownMenuItem>\n              <DropdownMenuSeparator />\n              <DropdownMenuItem>View customer</DropdownMenuItem>\n              <DropdownMenuItem>View payment details</DropdownMenuItem>\n            </DropdownMenuContent>\n          </DropdownMenu>\n        </div>\n      );\n    }\n  }\n];\n\nexport default function DataTableDemo() {\n  const [data] = React.useState<Payment[]>(initialData);\n  const [sorting, setSorting] = React.useState<SortingState>([]);\n  const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>([]);\n  const [columnVisibility, setColumnVisibility] = React.useState<VisibilityState>({});\n  const [rowSelection, setRowSelection] = React.useState({});\n  const [columnOrder, setColumnOrder] = React.useState<string[]>(() =>\n    baseColumns\n      .map((col) => (col.id || (\"accessorKey\" in col ? col.accessorKey : null)) as string)\n      .filter((id) => id && id !== \"select\" && id !== \"actions\")\n  );\n  const sortableId = React.useId();\n\n  const sensors = useSensors(\n    useSensor(MouseSensor, {}),\n    useSensor(TouchSensor, {}),\n    useSensor(KeyboardSensor, {})\n  );\n\n  const columns = React.useMemo<ColumnDef<Payment>[]>(() => {\n    const selectColumn = baseColumns.find((col) => col.id === \"select\");\n    const actionsColumn = baseColumns.find((col) => col.id === \"actions\");\n    const otherColumns = columnOrder\n      .map((colId) =>\n        baseColumns.find(\n          (col) => col.id === colId || (\"accessorKey\" in col && col.accessorKey === colId)\n        )\n      )\n      .filter((col): col is ColumnDef<Payment> => col !== undefined);\n    const result: ColumnDef<Payment>[] = [];\n    if (selectColumn) result.push(selectColumn);\n    result.push(...otherColumns);\n    if (actionsColumn) result.push(actionsColumn);\n    return result;\n  }, [columnOrder]);\n\n  const fullColumnOrder = React.useMemo<string[]>(() => {\n    return [\"select\", ...columnOrder, \"actions\"];\n  }, [columnOrder]);\n\n  const table = useReactTable({\n    data,\n    columns,\n    columnResizeMode: \"onChange\",\n    getRowId: (row) => row.id,\n    onSortingChange: setSorting,\n    onColumnFiltersChange: setColumnFilters,\n    getCoreRowModel: getCoreRowModel(),\n    getPaginationRowModel: getPaginationRowModel(),\n    getSortedRowModel: getSortedRowModel(),\n    getFilteredRowModel: getFilteredRowModel(),\n    onColumnVisibilityChange: setColumnVisibility,\n    onRowSelectionChange: setRowSelection,\n    onColumnOrderChange: (updater) => {\n      const newOrder = typeof updater === \"function\" ? updater(fullColumnOrder) : updater;\n      const filteredOrder = newOrder.filter((id: string) => id !== \"select\" && id !== \"actions\");\n      setColumnOrder(filteredOrder);\n    },\n    state: {\n      sorting,\n      columnFilters,\n      columnVisibility,\n      rowSelection,\n      columnOrder: fullColumnOrder\n    }\n  });\n\n  function handleDragEnd(event: DragEndEvent) {\n    const { active, over } = event;\n    if (\n      active &&\n      over &&\n      active.id !== over.id &&\n      active.id !== \"select\" &&\n      active.id !== \"actions\" &&\n      over.id !== \"select\" &&\n      over.id !== \"actions\"\n    ) {\n      setColumnOrder((order) => {\n        const oldIndex = order.indexOf(active.id as string);\n        const newIndex = order.indexOf(over.id as string);\n        return arrayMove(order, oldIndex, newIndex);\n      });\n    }\n  }\n\n  return (\n    <div className=\"w-full max-w-6xl space-y-4\">\n      <div className=\"flex items-center gap-2\">\n        <InputGroup>\n          <InputGroupInput\n            placeholder=\"Filter emails...\"\n            value={(table.getColumn(\"email\")?.getFilterValue() as string) ?? \"\"}\n            onChange={(event) => table.getColumn(\"email\")?.setFilterValue(event.target.value)}\n          />\n          <InputGroupAddon>\n            <SearchIcon />\n          </InputGroupAddon>\n        </InputGroup>\n        <div className=\"ml-auto flex items-center gap-2\">\n          {table.getFilteredSelectedRowModel().rows.length > 0 && (\n            <DropdownMenu>\n              <DropdownMenuTrigger asChild>\n                <Button variant=\"outline\">\n                  Actions\n                  <ChevronDown />\n                </Button>\n              </DropdownMenuTrigger>\n              <DropdownMenuContent align=\"end\">\n                <DropdownMenuLabel>\n                  {table.getFilteredSelectedRowModel().rows.length} selected\n                </DropdownMenuLabel>\n                <DropdownMenuSeparator />\n                <DropdownMenuItem>\n                  <Copy />\n                  Copy selected IDs\n                </DropdownMenuItem>\n                <DropdownMenuItem>\n                  <FileText />\n                  Copy as JSON\n                </DropdownMenuItem>\n                <DropdownMenuSeparator />\n                <DropdownMenuItem>\n                  <Download />\n                  Export as CSV\n                </DropdownMenuItem>\n                <DropdownMenuSeparator />\n                <DropdownMenuItem className=\"text-destructive focus:text-destructive\">\n                  <Trash2 />\n                  Delete selected\n                </DropdownMenuItem>\n              </DropdownMenuContent>\n            </DropdownMenu>\n          )}\n          <DropdownMenu>\n            <DropdownMenuTrigger asChild>\n              <Button variant=\"outline\">\n                <ColumnsIcon />\n              </Button>\n            </DropdownMenuTrigger>\n            <DropdownMenuContent align=\"end\">\n              {table\n                .getAllColumns()\n                .filter((column) => column.getCanHide())\n                .map((column) => {\n                  return (\n                    <DropdownMenuCheckboxItem\n                      key={column.id}\n                      className=\"capitalize\"\n                      checked={column.getIsVisible()}\n                      onCheckedChange={(value) => column.toggleVisibility(!!value)}>\n                      {column.id}\n                    </DropdownMenuCheckboxItem>\n                  );\n                })}\n            </DropdownMenuContent>\n          </DropdownMenu>\n        </div>\n      </div>\n      <div className=\"overflow-hidden rounded-md border\">\n        <DndContext\n          id={sortableId}\n          sensors={sensors}\n          collisionDetection={closestCenter}\n          modifiers={[restrictToHorizontalAxis]}\n          onDragEnd={handleDragEnd}>\n          <Table>\n            <TableHeader>\n              {table.getHeaderGroups().map((headerGroup) => (\n                <TableRow key={headerGroup.id} className=\"bg-muted/50 [&>th]:border-t-0\">\n                  <SortableContext items={columnOrder} strategy={horizontalListSortingStrategy}>\n                    {headerGroup.headers.map((header) => (\n                      <DraggableHeader key={header.id} header={header} />\n                    ))}\n                  </SortableContext>\n                </TableRow>\n              ))}\n            </TableHeader>\n            <TableBody>\n              {table.getRowModel().rows?.length ? (\n                table.getRowModel().rows.map((row) => (\n                  <TableRow key={row.id} data-state={row.getIsSelected() && \"selected\"}>\n                    <SortableContext items={columnOrder} strategy={horizontalListSortingStrategy}>\n                      {row.getVisibleCells().map((cell) => (\n                        <DragAlongCell key={cell.id} cell={cell} />\n                      ))}\n                    </SortableContext>\n                  </TableRow>\n                ))\n              ) : (\n                <TableRow>\n                  <TableCell colSpan={columns.length} className=\"h-24 text-center\">\n                    No results.\n                  </TableCell>\n                </TableRow>\n              )}\n            </TableBody>\n          </Table>\n        </DndContext>\n      </div>\n      <div className=\"flex items-center justify-end space-x-2\">\n        <div className=\"text-muted-foreground flex-1 text-sm\">\n          {table.getFilteredSelectedRowModel().rows.length} of{\" \"}\n          {table.getFilteredRowModel().rows.length} row(s) selected.\n        </div>\n        <div className=\"space-x-2\">\n          <Button\n            variant=\"outline\"\n            size=\"sm\"\n            onClick={() => table.previousPage()}\n            disabled={!table.getCanPreviousPage()}>\n            Previous\n          </Button>\n          <Button\n            variant=\"outline\"\n            size=\"sm\"\n            onClick={() => table.nextPage()}\n            disabled={!table.getCanNextPage()}>\n            Next\n          </Button>\n        </div>\n      </div>\n    </div>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/data-table5.tsx"
    }
  ],
  "type": "registry:component"
}