{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "data-table-demo",
  "title": "Data Table Demo",
  "description": "Demo usage for the Data Table component",
  "registryDependencies": [
    "@spectrumui/data-table"
  ],
  "files": [
    {
      "path": "app/registry/data-table/data-table-demo.tsx",
      "content": "'use client';\n\nimport * as React from 'react';\n\nimport { DataTable, type DataTableColumn } from '@/components/spectrumui/data-table';\n\n/* Iconly / Bold icons, filled with currentColor. */\ntype IconProps = React.SVGProps<SVGSVGElement>;\n\nfunction Glyph({ children, ...props }: IconProps) {\n  return (\n    <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" aria-hidden=\"true\" {...props}>\n      {children}\n    </svg>\n  );\n}\n\n/** Iconly / Bold / More Circle. */\nfunction IconMore(props: IconProps) {\n  return (\n    <Glyph {...props}>\n      <path\n        transform=\"translate(2, 2)\"\n        d=\"M10,0 C15.52,0 20,4.48 20,10 C20,15.52 15.52,20 10,20 C4.47,20 0,15.52 0,10 C0,4.48 4.47,0 10,0 Z M14.48,8.801 C13.81,8.801 13.28,9.34 13.28,10 C13.28,10.66 13.81,11.2 14.48,11.2 C15.14,11.2 15.67,10.66 15.67,10 C15.67,9.34 15.14,8.801 14.48,8.801 Z M10,8.801 C9.34,8.801 8.8,9.34 8.8,10 C8.8,10.66 9.34,11.2 10,11.2 C10.66,11.2 11.19,10.66 11.19,10 C11.19,9.34 10.66,8.801 10,8.801 Z M5.52,8.801 C4.86,8.801 4.32,9.34 4.32,10 C4.32,10.66 4.86,11.2 5.52,11.2 C6.18,11.2 6.71,10.66 6.71,10 C6.71,9.34 6.18,8.801 5.52,8.801 Z\"\n      />\n    </Glyph>\n  );\n}\n\n/** Iconly / Bold / Download. */\nfunction IconDownload(props: IconProps) {\n  return (\n    <Glyph {...props}>\n      <path\n        transform=\"translate(2, 2.5)\"\n        d=\"M9.23,4.791 L9.23,0.782 C9.23,0.355 9.57,0 10,0 C10.385,0 10.711,0.298 10.763,0.677 L10.77,0.782 L10.77,4.791 L15.55,4.791 C17.93,4.791 19.885,6.74 19.995,9.17 L20,9.386 L20,14.425 C20,16.873 18.113,18.882 15.768,18.995 L15.56,19 L4.44,19 C2.06,19 0.114,17.061 0.005,14.621 L0,14.405 L0,9.376 C0,6.928 1.878,4.909 4.222,4.796 L4.43,4.791 L9.23,4.791 L9.23,11.193 L7.63,9.541 C7.33,9.231 6.84,9.231 6.54,9.541 C6.39,9.696 6.32,9.902 6.32,10.109 C6.32,10.266 6.365,10.429 6.46,10.568 L6.54,10.667 L9.45,13.682 C9.59,13.837 9.79,13.919 10,13.919 C10.167,13.919 10.333,13.862 10.465,13.753 L10.54,13.682 L13.45,10.667 C13.75,10.357 13.75,9.851 13.45,9.541 C13.177,9.259 12.748,9.234 12.446,9.464 L12.36,9.541 L10.77,11.193 L10.77,4.791 L9.23,4.791 Z\"\n      />\n    </Glyph>\n  );\n}\n\n/** Iconly / Bold / Delete. */\nfunction IconDelete(props: IconProps) {\n  return (\n    <Glyph {...props}>\n      <path\n        transform=\"translate(3, 2)\"\n        d=\"M15.939,6.697 C16.138,6.697 16.319,6.784 16.462,6.931 C16.596,7.088 16.663,7.283 16.643,7.489 C16.643,7.557 16.11,14.297 15.806,17.134 C15.615,18.875 14.493,19.932 12.809,19.961 C11.515,19.99 10.25,20 9.004,20 C7.681,20 6.388,19.99 5.132,19.961 C3.505,19.922 2.382,18.846 2.201,17.134 C1.888,14.287 1.364,7.557 1.355,7.489 C1.345,7.283 1.411,7.088 1.545,6.931 C1.678,6.784 1.868,6.697 2.069,6.697 L15.939,6.697 Z M11.065,0 C11.949,0 12.738,0.617 12.967,1.497 L12.967,1.497 L13.13,2.227 C13.263,2.822 13.778,3.243 14.371,3.243 L14.371,3.243 L17.287,3.243 C17.676,3.243 18,3.566 18,3.977 L18,3.977 L18,4.357 C18,4.758 17.676,5.091 17.287,5.091 L17.287,5.091 L0.714,5.091 C0.324,5.091 0,4.758 0,4.357 L0,4.357 L0,3.977 C0,3.566 0.324,3.243 0.714,3.243 L0.714,3.243 L3.63,3.243 C4.222,3.243 4.737,2.822 4.871,2.228 L4.871,2.228 L5.023,1.546 C5.261,0.617 6.041,0 6.935,0 L6.935,0 Z\"\n      />\n    </Glyph>\n  );\n}\n\ntype PaymentStatus = 'paid' | 'pending' | 'failed' | 'refunded';\n\ninterface Payment {\n  id: string;\n  customer: string;\n  email: string;\n  avatar: string;\n  status: PaymentStatus;\n  method: string;\n  date: string;\n  amount: number;\n  plan: string;\n  seats: number;\n}\n\nconst PAYMENTS: Payment[] = [\n  {\n    id: 'in_4021',\n    customer: 'Marta Halapin',\n    email: 'marta@northsail.co',\n    avatar: '/avatars/people/01.jpg',\n    status: 'paid',\n    method: 'Visa •• 4242',\n    date: '2026-09-08',\n    amount: 1840,\n    plan: 'Scale (annual)',\n    seats: 24,\n  },\n  {\n    id: 'in_4020',\n    customer: 'Devansh Rao',\n    email: 'devansh@quillbase.io',\n    avatar: '/avatars/people/02.jpg',\n    status: 'pending',\n    method: 'ACH transfer',\n    date: '2026-09-08',\n    amount: 420,\n    plan: 'Team (monthly)',\n    seats: 6,\n  },\n  {\n    id: 'in_4019',\n    customer: 'Ines Almeida',\n    email: 'ines@fold.studio',\n    avatar: '/avatars/people/03.jpg',\n    status: 'paid',\n    method: 'Mastercard •• 8801',\n    date: '2026-09-07',\n    amount: 96,\n    plan: 'Starter (monthly)',\n    seats: 2,\n  },\n  {\n    id: 'in_4018',\n    customer: 'Tobias Werner',\n    email: 'tobias@heliolab.de',\n    avatar: '/avatars/people/04.jpg',\n    status: 'failed',\n    method: 'Visa •• 1119',\n    date: '2026-09-07',\n    amount: 2400,\n    plan: 'Scale (annual)',\n    seats: 32,\n  },\n  {\n    id: 'in_4017',\n    customer: 'Amara Okonjo',\n    email: 'amara@brightpath.ng',\n    avatar: '/avatars/people/05.jpg',\n    status: 'paid',\n    method: 'Amex •• 3005',\n    date: '2026-09-06',\n    amount: 780,\n    plan: 'Team (annual)',\n    seats: 12,\n  },\n  {\n    id: 'in_4016',\n    customer: 'Rafael Costa',\n    email: 'rafael@medela.com.br',\n    avatar: '/avatars/people/06.jpg',\n    status: 'refunded',\n    method: 'Visa •• 6710',\n    date: '2026-09-05',\n    amount: 96,\n    plan: 'Starter (monthly)',\n    seats: 2,\n  },\n  {\n    id: 'in_4015',\n    customer: 'Suki Tanaka',\n    email: 'suki@kirin-works.jp',\n    avatar: '/avatars/people/07.jpg',\n    status: 'paid',\n    method: 'Wire transfer',\n    date: '2026-09-05',\n    amount: 5600,\n    plan: 'Enterprise',\n    seats: 140,\n  },\n  {\n    id: 'in_4014',\n    customer: 'Elliot Brandt',\n    email: 'elliot@zoning.app',\n    avatar: '/avatars/people/08.jpg',\n    status: 'pending',\n    method: 'ACH transfer',\n    date: '2026-09-04',\n    amount: 320,\n    plan: 'Team (monthly)',\n    seats: 5,\n  },\n  {\n    id: 'in_4013',\n    customer: 'Nour Haddad',\n    email: 'nour@carbonline.me',\n    avatar: '/avatars/people/09.jpg',\n    status: 'paid',\n    method: 'Mastercard •• 2260',\n    date: '2026-09-03',\n    amount: 1120,\n    plan: 'Scale (monthly)',\n    seats: 18,\n  },\n  {\n    id: 'in_4012',\n    customer: 'Priya Venkat',\n    email: 'priya@loomstack.in',\n    avatar: '/avatars/people/10.jpg',\n    status: 'paid',\n    method: 'Visa •• 9033',\n    date: '2026-09-02',\n    amount: 240,\n    plan: 'Team (monthly)',\n    seats: 4,\n  },\n  {\n    id: 'in_4011',\n    customer: 'Kwame Mensah',\n    email: 'kwame@arcfield.gh',\n    avatar: '/avatars/people/11.jpg',\n    status: 'failed',\n    method: 'Visa •• 4477',\n    date: '2026-09-02',\n    amount: 780,\n    plan: 'Team (annual)',\n    seats: 12,\n  },\n  {\n    id: 'in_4010',\n    customer: 'Lena Fischer',\n    email: 'lena@paperkite.ch',\n    avatar: '/avatars/people/12.jpg',\n    status: 'paid',\n    method: 'Amex •• 7712',\n    date: '2026-09-01',\n    amount: 3200,\n    plan: 'Enterprise',\n    seats: 80,\n  },\n  {\n    id: 'in_4009',\n    customer: 'Owen Whitcombe',\n    email: 'owen@tidepool.uk',\n    avatar: '/avatars/people/13.jpg',\n    status: 'paid',\n    method: 'Visa •• 5521',\n    date: '2026-08-31',\n    amount: 96,\n    plan: 'Starter (monthly)',\n    seats: 1,\n  },\n  {\n    id: 'in_4008',\n    customer: 'Sara Lindqvist',\n    email: 'sara@nordform.se',\n    avatar: '/avatars/people/14.jpg',\n    status: 'refunded',\n    method: 'Mastercard •• 1044',\n    date: '2026-08-30',\n    amount: 420,\n    plan: 'Team (monthly)',\n    seats: 6,\n  },\n];\n\nconst STATUS_STYLE: Record<PaymentStatus, string> = {\n  paid: 'bg-emerald-50 text-emerald-700 ring-emerald-600/20 dark:bg-emerald-400/10 dark:text-emerald-300 dark:ring-emerald-300/20',\n  pending:\n    'bg-amber-50 text-amber-800 ring-amber-600/20 dark:bg-amber-400/10 dark:text-amber-300 dark:ring-amber-300/25',\n  failed:\n    'bg-rose-50 text-rose-700 ring-rose-600/20 dark:bg-rose-400/10 dark:text-rose-300 dark:ring-rose-300/25',\n  refunded:\n    'bg-neutral-100 text-neutral-600 ring-neutral-500/20 dark:bg-neutral-100/10 dark:text-neutral-300 dark:ring-white/15',\n};\n\nconst money = new Intl.NumberFormat('en-US', {\n  style: 'currency',\n  currency: 'USD',\n  maximumFractionDigits: 0,\n});\n\nconst MONTHS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];\n\n/** Formatted by hand rather than by locale, so the server and client agree. */\nfunction shortDate(iso: string) {\n  const [, month, day] = iso.split('-');\n  return `${MONTHS[Number(month) - 1]} ${Number(day)}`;\n}\n\nfunction StatusPill({ status }: { status: PaymentStatus }) {\n  return (\n    <span\n      className={`inline-flex items-center gap-1.5 rounded-full px-2 py-0.5 text-xs font-medium capitalize ring-1 ring-inset ${STATUS_STYLE[status]}`}\n    >\n      <span aria-hidden=\"true\" className=\"size-1.5 rounded-full bg-current\" />\n      {status}\n    </span>\n  );\n}\n\nconst columns: DataTableColumn<Payment>[] = [\n  {\n    id: 'customer',\n    header: 'Customer',\n    sortable: true,\n    value: (row) => row.customer,\n    cell: (row) => (\n      <div className=\"flex items-center gap-2.5\">\n        {/* eslint-disable-next-line @next/next/no-img-element */}\n        <img\n          src={row.avatar}\n          alt=\"\"\n          loading=\"lazy\"\n          width={28}\n          height={28}\n          className=\"size-7 shrink-0 rounded-full object-cover outline outline-1 -outline-offset-1 outline-black/10 dark:outline-white/10\"\n        />\n        <span className=\"min-w-0\">\n          <span className=\"block truncate\">{row.customer}</span>\n          <span className=\"block truncate text-xs font-normal text-neutral-500 dark:text-neutral-400\">\n            {row.email}\n          </span>\n        </span>\n      </div>\n    ),\n  },\n  {\n    id: 'status',\n    header: 'Status',\n    sortable: true,\n    value: (row) => row.status,\n    cell: (row) => <StatusPill status={row.status} />,\n  },\n  {\n    id: 'date',\n    header: 'Date',\n    sortable: true,\n    hideBelow: 'sm',\n    value: (row) => row.date,\n    cell: (row) => <span className=\"tabular-nums\">{shortDate(row.date)}</span>,\n  },\n  {\n    id: 'amount',\n    header: 'Amount',\n    sortable: true,\n    numeric: true,\n    value: (row) => row.amount,\n    formatTotal: (sum) => money.format(sum),\n    cell: (row) => (\n      <span className=\"font-medium text-neutral-900 dark:text-neutral-100\">\n        {money.format(row.amount)}\n      </span>\n    ),\n  },\n];\n\nexport default function DataTableDemo() {\n  const [rows, setRows] = React.useState(PAYMENTS);\n  const removed = PAYMENTS.length - rows.length;\n\n  return (\n    <div className=\"w-full py-4\">\n      <DataTable\n        data={rows}\n        columns={columns}\n        rowId={(row) => row.id}\n        rowLabel={(row) => `payment from ${row.customer}`}\n        caption=\"Payments from the last two weeks, with status, method, date and amount.\"\n        quickFilter={{ columnId: 'status', label: 'Filter by status' }}\n        totals={['amount']}\n        onDelete={(ids) => setRows((current) => current.filter((row) => !ids.includes(row.id)))}\n        toolbar={\n          <>\n            {removed > 0 && (\n              <button\n                type=\"button\"\n                onClick={() => setRows(PAYMENTS)}\n                className=\"rounded-full px-2 text-sm font-medium text-neutral-500 underline underline-offset-2 transition-colors duration-100 ease-out hover:text-neutral-900 focus-visible:ring-1 focus-visible:ring-neutral-950 focus-visible:outline-hidden dark:text-neutral-400 dark:hover:text-neutral-100 dark:focus-visible:ring-neutral-300\"\n              >\n                Restore {removed}\n              </button>\n            )}\n            <button\n              type=\"button\"\n              className=\"flex h-9 items-center gap-1.5 rounded-full bg-neutral-900 px-4 text-sm font-medium text-neutral-100 transition-colors duration-100 ease-out hover:bg-neutral-800 focus-visible:ring-1 focus-visible:ring-neutral-950 focus-visible:ring-offset-2 focus-visible:outline-hidden dark:bg-neutral-100 dark:text-neutral-900 dark:hover:bg-neutral-200 dark:focus-visible:ring-neutral-300\"\n            >\n              <IconDownload className=\"size-4\" />\n              Export\n            </button>\n          </>\n        }\n        selectable\n        defaultSort={{ columnId: 'date', direction: 'desc' }}\n        pageSize={6}\n        renderDetail={(row) => (\n          <dl className=\"grid grid-cols-2 gap-x-8 gap-y-3 text-sm sm:grid-cols-4\">\n            {[\n              ['Invoice', row.id],\n              ['Plan', row.plan],\n              ['Seats', String(row.seats)],\n              ['Method', row.method],\n            ].map(([label, value]) => (\n              <div key={label}>\n                <dt className=\"text-neutral-500 dark:text-neutral-400\">{label}</dt>\n                <dd className=\"mt-0.5 font-medium text-neutral-900 tabular-nums dark:text-neutral-100\">\n                  {value}\n                </dd>\n              </div>\n            ))}\n          </dl>\n        )}\n        rowActions={(row) => (\n          <button\n            type=\"button\"\n            aria-label={`Open actions for ${row.customer}`}\n            className=\"grid size-8 place-items-center rounded-xl text-neutral-400 transition-colors duration-100 ease-out hover:bg-neutral-100 hover:text-neutral-900 focus-visible:ring-1 focus-visible:ring-neutral-950 focus-visible:outline-hidden dark:hover:bg-neutral-800 dark:hover:text-neutral-100 dark:focus-visible:ring-neutral-300\"\n          >\n            <IconMore className=\"size-4\" />\n          </button>\n        )}\n        bulkActions={({ ids, clear, remove }) => (\n          <>\n            <button\n              type=\"button\"\n              onClick={clear}\n              aria-label={`Export ${ids.length} payments`}\n              className=\"flex items-center gap-1.5 rounded-full px-2.5 py-1.5 text-sm font-medium transition-colors duration-100 ease-out hover:bg-white/10 focus-visible:ring-1 focus-visible:ring-current focus-visible:outline-hidden dark:hover:bg-black/10\"\n            >\n              <IconDownload className=\"size-4\" />\n              <span className=\"hidden sm:inline\">Export</span>\n            </button>\n            <button\n              type=\"button\"\n              onClick={remove}\n              aria-label={`Delete ${ids.length} payments`}\n              className=\"flex items-center gap-1.5 rounded-full px-2.5 py-1.5 text-sm font-medium text-rose-300 transition-colors duration-100 ease-out hover:bg-white/10 focus-visible:ring-1 focus-visible:ring-current focus-visible:outline-hidden dark:text-rose-700 dark:hover:bg-black/10\"\n            >\n              <IconDelete className=\"size-4\" />\n              <span className=\"hidden sm:inline\">Delete</span>\n            </button>\n          </>\n        )}\n      />\n    </div>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/spectrumui/data-table-demo.tsx"
    }
  ],
  "type": "registry:component"
}
