{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "retro-data-table",
  "title": "RetroDataTable",
  "description": "A Mac OS 9 styled full-featured data table with column sorting, text filtering, pagination, row selection, and column visibility, built on @tanstack/react-table and existing RetroTable primitives.",
  "dependencies": [
    "@tanstack/react-table"
  ],
  "registryDependencies": [
    "https://nostalgia-ui.com/r/nostalgia-theme.json",
    "https://nostalgia-ui.com/r/retro-table.json"
  ],
  "files": [
    {
      "path": "registry/new-york/ui/retro-data-table.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport {\n  type Column,\n  type ColumnDef,\n  type ColumnFiltersState,\n  type SortingState,\n  type Table as TanstackTable,\n  type VisibilityState,\n  flexRender,\n  getCoreRowModel,\n  getFilteredRowModel,\n  getPaginationRowModel,\n  getSortedRowModel,\n  useReactTable,\n} from \"@tanstack/react-table\"\n\nimport { cn } from \"@/lib/utils\"\nimport {\n  RetroTable,\n  RetroTableBody,\n  RetroTableCell,\n  RetroTableHead,\n  RetroTableHeader,\n  RetroTableRow,\n} from \"@/registry/new-york/ui/retro-table\"\n\n/* ------------------------------------------------------------------ */\n/*  Sort arrow SVGs                                                    */\n/* ------------------------------------------------------------------ */\n\nfunction SortAscIcon({ className }: { className?: string }) {\n  return (\n    <svg\n      width=\"6\"\n      height=\"4\"\n      viewBox=\"0 0 6 4\"\n      fill=\"currentColor\"\n      className={className}\n      aria-hidden=\"true\"\n    >\n      <path d=\"M3 0L6 4H0z\" />\n    </svg>\n  )\n}\n\nfunction SortDescIcon({ className }: { className?: string }) {\n  return (\n    <svg\n      width=\"6\"\n      height=\"4\"\n      viewBox=\"0 0 6 4\"\n      fill=\"currentColor\"\n      className={className}\n      aria-hidden=\"true\"\n    >\n      <path d=\"M3 4L0 0h6z\" />\n    </svg>\n  )\n}\n\n/* ------------------------------------------------------------------ */\n/*  RetroDataTableColumnHeader                                         */\n/* ------------------------------------------------------------------ */\n\ninterface RetroDataTableColumnHeaderProps<TData, TValue> {\n  column: Column<TData, TValue>\n  title: string\n  className?: string\n}\n\nfunction RetroDataTableColumnHeader<TData, TValue>({\n  column,\n  title,\n  className,\n}: RetroDataTableColumnHeaderProps<TData, TValue>) {\n  if (!column.getCanSort()) {\n    return <div className={cn(className)}>{title}</div>\n  }\n\n  const sorted = column.getIsSorted()\n\n  return (\n    <button\n      type=\"button\"\n      className={cn(\n        \"inline-flex items-center gap-1 cursor-pointer select-none\",\n        \"font-[family-name:var(--os9-font-heading)] text-[10px] font-bold\",\n        \"outline-none focus-visible:os9-focus-ring\",\n        className\n      )}\n      onClick={() => column.toggleSorting(sorted === \"asc\")}\n    >\n      {title}\n      <span className=\"inline-flex flex-col gap-px\">\n        <SortAscIcon\n          className={cn(\n            sorted === \"asc\" ? \"opacity-100\" : \"opacity-30\"\n          )}\n        />\n        <SortDescIcon\n          className={cn(\n            sorted === \"desc\" ? \"opacity-100\" : \"opacity-30\"\n          )}\n        />\n      </span>\n    </button>\n  )\n}\n\n/* ------------------------------------------------------------------ */\n/*  RetroDataTableToolbar                                              */\n/* ------------------------------------------------------------------ */\n\ninterface RetroDataTableToolbarProps<TData>\n  extends React.HTMLAttributes<HTMLDivElement> {\n  table: TanstackTable<TData>\n  filterColumn?: string\n  filterPlaceholder?: string\n}\n\nfunction RetroDataTableToolbar<TData>({\n  table,\n  filterColumn,\n  filterPlaceholder = \"Filter...\",\n  className,\n  children,\n  ...props\n}: RetroDataTableToolbarProps<TData>) {\n  const column = filterColumn ? table.getColumn(filterColumn) : undefined\n\n  return (\n    <div\n      className={cn(\"flex items-center gap-2 pb-2\", className)}\n      {...props}\n    >\n      {column && (\n        <input\n          type=\"text\"\n          placeholder={filterPlaceholder}\n          aria-label={filterPlaceholder.replace(/\\.{3}$|…$/, \"\") || \"Filter\"}\n          value={(column.getFilterValue() as string) ?? \"\"}\n          onChange={(e) => column.setFilterValue(e.target.value)}\n          className={cn(\n            \"w-full max-w-[200px]\",\n            \"border border-os9-black bg-os9-white text-os9-black\",\n            \"placeholder:text-os9-gray-600\",\n            \"font-[family-name:var(--os9-font-sans)] text-[10px] leading-normal\",\n            \"px-[5px] py-[3px]\",\n            \"outline-none\",\n            \"shadow-[inset_1px_1px_0_var(--os9-gray-700),inset_-1px_-1px_0_var(--os9-white),-1px_0_0_var(--os9-gray-700),0_-1px_0_var(--os9-gray-700),1px_0_0_var(--os9-white),0_1px_0_var(--os9-white)]\",\n            \"focus:shadow-[0_2px_0_0_var(--os9-focus),2px_0_0_0_var(--os9-focus),0_-2px_0_0_var(--os9-focus),-2px_0_0_0_var(--os9-focus),0_0_0_1px_var(--os9-focus)]\"\n          )}\n        />\n      )}\n      {children}\n    </div>\n  )\n}\n\n/* ------------------------------------------------------------------ */\n/*  RetroDataTablePagination                                           */\n/* ------------------------------------------------------------------ */\n\ninterface RetroDataTablePaginationProps<TData>\n  extends React.HTMLAttributes<HTMLDivElement> {\n  table: TanstackTable<TData>\n  showRowSelection?: boolean\n  pageSizes?: number[]\n}\n\n/** Shared OS9 button base styles */\nconst os9BtnBase = [\n  \"inline-flex items-center justify-center\",\n  \"h-[20px] px-2\",\n  \"font-[family-name:var(--os9-font-heading)] text-[10px] tracking-[0.42px] leading-[0.98]\",\n  \"border border-os9-black bg-os9-gray-300 text-os9-black\",\n  \"shadow-[inset_1px_1px_0_var(--os9-white),inset_-1px_-1px_0_var(--os9-gray-700)]\",\n  \"cursor-pointer select-none\",\n  \"outline-none focus-visible:os9-focus-ring\",\n  \"active:bg-os9-gray-800 active:text-os9-white\",\n  \"active:shadow-[inset_1px_1px_0_var(--os9-gray-700),inset_-1px_-1px_0_var(--os9-white)]\",\n] as const\n\nconst os9BtnDisabled = [\n  \"pointer-events-none\",\n  \"text-os9-gray-600\",\n  \"border-os9-gray-600\",\n  \"shadow-[inset_1px_1px_0_var(--os9-gray-300),inset_-1px_-1px_0_var(--os9-gray-400)]\",\n] as const\n\nfunction RetroDataTablePagination<TData>({\n  table,\n  showRowSelection = false,\n  pageSizes,\n  className,\n  ...props\n}: RetroDataTablePaginationProps<TData>) {\n  const pageIndex = table.getState().pagination.pageIndex\n  const pageCount = table.getPageCount()\n\n  return (\n    <div\n      className={cn(\n        \"flex items-center justify-between gap-4 pt-2\",\n        \"font-[family-name:var(--os9-font-sans)] text-[10px]\",\n        className\n      )}\n      {...props}\n    >\n      {/* Left: row selection info */}\n      <div className=\"flex-1 text-os9-gray-800\">\n        {showRowSelection && (\n          <span>\n            {table.getFilteredSelectedRowModel().rows.length} of{\" \"}\n            {table.getFilteredRowModel().rows.length} row(s) selected\n          </span>\n        )}\n      </div>\n\n      {/* Center: page size selector */}\n      {pageSizes && pageSizes.length > 0 && (\n        <div className=\"flex items-center gap-1\">\n          <span className=\"text-os9-gray-800\">Rows:</span>\n          <select\n            aria-label=\"Rows per page\"\n            value={table.getState().pagination.pageSize}\n            onChange={(e) => table.setPageSize(Number(e.target.value))}\n            className={cn(\n              \"h-[18px] px-1\",\n              \"border border-os9-black bg-os9-gray-300 text-os9-black\",\n              \"font-[family-name:var(--os9-font-sans)] text-[10px]\",\n              \"shadow-[inset_1px_1px_0_var(--os9-white),inset_-1px_-1px_0_var(--os9-gray-700)]\",\n              \"cursor-pointer outline-none focus-visible:os9-focus-ring\"\n            )}\n          >\n            {pageSizes.map((size) => (\n              <option key={size} value={size}>\n                {size}\n              </option>\n            ))}\n          </select>\n        </div>\n      )}\n\n      {/* Right: page info + buttons */}\n      <div className=\"flex items-center gap-2\">\n        <span className=\"text-os9-gray-800\">\n          Page {pageIndex + 1} of {pageCount || 1}\n        </span>\n\n        <button\n          type=\"button\"\n          className={cn(\n            os9BtnBase,\n            !table.getCanPreviousPage() && os9BtnDisabled\n          )}\n          onClick={() => table.previousPage()}\n          disabled={!table.getCanPreviousPage()}\n        >\n          Previous\n        </button>\n\n        <button\n          type=\"button\"\n          className={cn(\n            os9BtnBase,\n            !table.getCanNextPage() && os9BtnDisabled\n          )}\n          onClick={() => table.nextPage()}\n          disabled={!table.getCanNextPage()}\n        >\n          Next\n        </button>\n      </div>\n    </div>\n  )\n}\n\n/* ------------------------------------------------------------------ */\n/*  RetroDataTable                                                     */\n/* ------------------------------------------------------------------ */\n\ninterface RetroDataTableProps<TData, TValue> {\n  columns: ColumnDef<TData, TValue>[]\n  data: TData[]\n  filterColumn?: string\n  filterPlaceholder?: string\n  showToolbar?: boolean\n  showPagination?: boolean\n  showRowSelection?: boolean\n  pageSizes?: number[]\n  pageSize?: number\n  className?: string\n}\n\nfunction RetroDataTable<TData, TValue>({\n  columns,\n  data,\n  filterColumn,\n  filterPlaceholder,\n  showToolbar = true,\n  showPagination = true,\n  showRowSelection = false,\n  pageSizes,\n  pageSize = 10,\n  className,\n}: RetroDataTableProps<TData, TValue>) {\n  const [sorting, setSorting] = React.useState<SortingState>([])\n  const [columnFilters, setColumnFilters] =\n    React.useState<ColumnFiltersState>([])\n  const [columnVisibility, setColumnVisibility] =\n    React.useState<VisibilityState>({})\n  const [rowSelection, setRowSelection] = React.useState({})\n\n  const table = useReactTable({\n    data,\n    columns,\n    state: {\n      sorting,\n      columnFilters,\n      columnVisibility,\n      rowSelection,\n    },\n    onSortingChange: setSorting,\n    onColumnFiltersChange: setColumnFilters,\n    onColumnVisibilityChange: setColumnVisibility,\n    onRowSelectionChange: setRowSelection,\n    getCoreRowModel: getCoreRowModel(),\n    getSortedRowModel: getSortedRowModel(),\n    getFilteredRowModel: getFilteredRowModel(),\n    getPaginationRowModel: getPaginationRowModel(),\n    initialState: {\n      pagination: {\n        pageSize,\n      },\n    },\n  })\n\n  return (\n    <div className={cn(className)}>\n      {showToolbar && filterColumn && (\n        <RetroDataTableToolbar\n          table={table}\n          filterColumn={filterColumn}\n          filterPlaceholder={filterPlaceholder}\n        />\n      )}\n\n      <RetroTable>\n        <RetroTableHeader>\n          {table.getHeaderGroups().map((headerGroup) => (\n            <RetroTableRow key={headerGroup.id}>\n              {headerGroup.headers.map((header) => (\n                <RetroTableHead key={header.id}>\n                  {header.isPlaceholder\n                    ? null\n                    : flexRender(\n                        header.column.columnDef.header,\n                        header.getContext()\n                      )}\n                </RetroTableHead>\n              ))}\n            </RetroTableRow>\n          ))}\n        </RetroTableHeader>\n        <RetroTableBody>\n          {table.getRowModel().rows?.length ? (\n            table.getRowModel().rows.map((row) => (\n              <RetroTableRow\n                key={row.id}\n                data-state={row.getIsSelected() ? \"selected\" : undefined}\n              >\n                {row.getVisibleCells().map((cell) => (\n                  <RetroTableCell key={cell.id}>\n                    {flexRender(\n                      cell.column.columnDef.cell,\n                      cell.getContext()\n                    )}\n                  </RetroTableCell>\n                ))}\n              </RetroTableRow>\n            ))\n          ) : (\n            <RetroTableRow>\n              <RetroTableCell\n                colSpan={columns.length}\n                style={{ textAlign: \"center\", padding: \"16px 8px\" }}\n              >\n                No results.\n              </RetroTableCell>\n            </RetroTableRow>\n          )}\n        </RetroTableBody>\n      </RetroTable>\n\n      {showPagination && (\n        <RetroDataTablePagination\n          table={table}\n          showRowSelection={showRowSelection}\n          pageSizes={pageSizes}\n        />\n      )}\n    </div>\n  )\n}\n\n/* ------------------------------------------------------------------ */\n/*  Exports                                                            */\n/* ------------------------------------------------------------------ */\n\nexport {\n  RetroDataTable,\n  RetroDataTableColumnHeader,\n  RetroDataTableToolbar,\n  RetroDataTablePagination,\n}\n",
      "type": "registry:ui"
    }
  ],
  "type": "registry:ui"
}