{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "leaderboard-table",
  "title": "Leaderboard Table",
  "description": "Rankings with medal chips for the top three, player avatars, streaks and weekly movement.",
  "registryDependencies": [
    "@spectrumui/data-table"
  ],
  "files": [
    {
      "path": "components/spectrumui/blocks/tables/leaderboard-table.tsx",
      "content": "'use client';\n\nimport * as React from 'react';\nimport { DataTable, type DataTableColumn } from '@/components/spectrumui/data-table';\n\nfunction IconStar(props: React.SVGProps<SVGSVGElement>) {\n  return (\n    <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" aria-hidden=\"true\" {...props}>\n      <path\n        transform=\"translate(2, 2.5)\"\n        d=\"M15.919,11.82 C15.66,12.071 15.541,12.434 15.6,12.79 L16.489,17.71 C16.564,18.127 16.388,18.549 16.039,18.79 C15.697,19.04 15.242,19.07 14.869,18.87 L10.44,16.56 C10.286,16.478 10.115,16.434 9.94,16.429 L9.669,16.429 C9.575,16.443 9.483,16.473 9.399,16.519 L4.969,18.84 C4.75,18.95 4.502,18.989 4.259,18.95 C3.667,18.838 3.272,18.274 3.369,17.679 L4.259,12.759 C4.318,12.4 4.199,12.035 3.94,11.78 L0.329,8.28 C0.027,7.987 -0.078,7.547 0.06,7.15 C0.194,6.754 0.536,6.465 0.949,6.4 L5.919,5.679 C6.297,5.64 6.629,5.41 6.799,5.07 L8.989,0.58 C9.041,0.48 9.108,0.388 9.189,0.31 L9.279,0.24 C9.326,0.188 9.38,0.145 9.44,0.11 L9.549,0.07 L9.719,0 L10.14,0 C10.516,0.039 10.847,0.264 11.02,0.6 L13.239,5.07 C13.399,5.397 13.71,5.624 14.069,5.679 L19.039,6.4 C19.459,6.46 19.81,6.75 19.949,7.15 C20.08,7.551 19.967,7.991 19.659,8.28 L15.919,11.82 Z\"\n      />\n    </svg>\n  );\n}\n\nfunction DeltaChevron({ up, ...props }: { up: boolean } & React.SVGProps<SVGSVGElement>) {\n  return (\n    <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" aria-hidden=\"true\" {...props}>\n      <path\n        transform={up ? 'rotate(180 12 12) translate(6, 7)' : 'translate(6, 7)'}\n        d=\"M4.869,9.631 C4.811,9.574 4.563,9.361 4.359,9.162 C3.076,7.997 0.976,4.958 0.335,3.367 C0.232,3.125 0.014,2.514 0,2.188 C0,1.875 0.072,1.577 0.218,1.293 C0.422,0.938 0.743,0.654 1.122,0.498 C1.385,0.397 2.172,0.242 2.186,0.242 C3.047,0.086 4.446,0 5.992,0 C7.465,0 8.807,0.086 9.681,0.213 C9.695,0.228 10.673,0.384 11.008,0.554 C11.62,0.867 12,1.478 12,2.132 L12,2.188 C11.985,2.614 11.605,3.509 11.591,3.509 C10.949,5.014 8.952,7.983 7.625,9.177 C7.625,9.177 7.284,9.513 7.071,9.659 C6.765,9.887 6.386,10 6.007,10 C5.584,10 5.19,9.872 4.869,9.631\"\n      />\n    </svg>\n  );\n}\n\ninterface Player {\n  id: string;\n  rank: number;\n  name: string;\n  handle: string;\n  avatar: string;\n  score: number;\n  streak: number;\n  moved: number;\n}\n\nconst PLAYERS: Player[] = [\n  {\n    id: 'l1',\n    rank: 1,\n    name: 'Aiko Watanabe',\n    handle: '@aiko',\n    avatar: '/avatars/people/06.jpg',\n    score: 12840,\n    streak: 21,\n    moved: 0,\n  },\n  {\n    id: 'l2',\n    rank: 2,\n    name: 'Mateo Alvarez',\n    handle: '@mateo',\n    avatar: '/avatars/people/03.jpg',\n    score: 11960,\n    streak: 14,\n    moved: 2,\n  },\n  {\n    id: 'l3',\n    rank: 3,\n    name: 'Freya Lindgren',\n    handle: '@freya',\n    avatar: '/avatars/people/01.jpg',\n    score: 11205,\n    streak: 9,\n    moved: -1,\n  },\n  {\n    id: 'l4',\n    rank: 4,\n    name: 'Zara Osei',\n    handle: '@zara',\n    avatar: '/avatars/people/12.jpg',\n    score: 9871,\n    streak: 17,\n    moved: 1,\n  },\n  {\n    id: 'l5',\n    rank: 5,\n    name: 'Tomas Vega',\n    handle: '@tomas',\n    avatar: '/avatars/people/08.jpg',\n    score: 9412,\n    streak: 4,\n    moved: -2,\n  },\n  {\n    id: 'l6',\n    rank: 6,\n    name: 'Leila Haddad',\n    handle: '@leila',\n    avatar: '/avatars/people/05.jpg',\n    score: 8730,\n    streak: 11,\n    moved: 0,\n  },\n  {\n    id: 'l7',\n    rank: 7,\n    name: 'Nils Bakker',\n    handle: '@nils',\n    avatar: '/avatars/people/13.jpg',\n    score: 8104,\n    streak: 2,\n    moved: 3,\n  },\n];\n\nconst MEDAL: Record<number, string> = {\n  1: 'bg-amber-50 text-amber-700 ring-amber-600/25 dark:bg-amber-400/10 dark:text-amber-300 dark:ring-amber-300/25',\n  2: 'bg-neutral-100 text-neutral-600 ring-neutral-500/20 dark:bg-neutral-100/10 dark:text-neutral-300 dark:ring-white/15',\n  3: 'bg-orange-50 text-orange-700 ring-orange-600/20 dark:bg-orange-400/10 dark:text-orange-300 dark:ring-orange-300/25',\n};\n\nconst columns: DataTableColumn<Player>[] = [\n  {\n    id: 'rank',\n    header: '#',\n    sortable: true,\n    numeric: true,\n    align: 'center',\n    width: 56,\n    value: (row) => row.rank,\n    cell: (row) =>\n      row.rank <= 3 ? (\n        <span\n          className={`inline-flex size-6 items-center justify-center rounded-full text-xs font-semibold ring-1 ring-inset ${MEDAL[row.rank]}`}\n        >\n          {row.rank}\n        </span>\n      ) : (\n        <span className=\"tabular-nums text-neutral-500 dark:text-neutral-400\">{row.rank}</span>\n      ),\n  },\n  {\n    id: 'name',\n    header: 'Player',\n    sortable: true,\n    value: (row) => row.name,\n    cell: (row) => (\n      <span 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.name}</span>\n          <span className=\"block truncate text-xs font-normal text-neutral-500 dark:text-neutral-400\">\n            {row.handle}\n          </span>\n        </span>\n      </span>\n    ),\n  },\n  {\n    id: 'score',\n    header: 'Score',\n    sortable: true,\n    numeric: true,\n    value: (row) => row.score,\n    cell: (row) => (\n      <span className=\"font-medium text-neutral-900 dark:text-neutral-100\">\n        {row.score.toLocaleString('en-US')}\n      </span>\n    ),\n  },\n  {\n    id: 'streak',\n    header: 'Streak',\n    sortable: true,\n    numeric: true,\n    hideBelow: 'sm',\n    value: (row) => row.streak,\n    cell: (row) => (\n      <span className=\"inline-flex items-center gap-1.5 tabular-nums\">\n        <IconStar className=\"size-3.5 text-amber-500 dark:text-amber-400\" />\n        {row.streak} days\n      </span>\n    ),\n  },\n  {\n    id: 'moved',\n    header: 'This week',\n    numeric: true,\n    hideBelow: 'md',\n    value: (row) => row.moved,\n    cell: (row) =>\n      row.moved === 0 ? (\n        <span className=\"text-neutral-400 dark:text-neutral-500\">—</span>\n      ) : (\n        <span\n          className={`inline-flex items-center gap-1 font-medium tabular-nums ${\n            row.moved > 0\n              ? 'text-emerald-700 dark:text-emerald-300'\n              : 'text-rose-700 dark:text-rose-300'\n          }`}\n        >\n          <DeltaChevron up={row.moved > 0} className=\"size-3\" />\n          {Math.abs(row.moved)}\n        </span>\n      ),\n  },\n];\n\nexport function LeaderboardTable({\n  variant = 'Comfortable',\n}: {\n  variant?: 'Comfortable' | 'Striped';\n}) {\n  return (\n    <div className=\"mx-auto w-full max-w-3xl px-4 py-10 sm:px-6\">\n      <DataTable\n        data={PLAYERS}\n        columns={columns}\n        rowId={(row) => row.id}\n        rowLabel={(row) => row.name}\n        caption=\"Weekly leaderboard with rank medals, scores, streaks and movement.\"\n        variant={variant === 'Striped' ? 'striped' : 'panel'}\n        title=\"Leaderboard\"\n        defaultSort={{ columnId: 'rank', direction: 'asc' }}\n      />\n    </div>\n  );\n}\n",
      "type": "registry:block",
      "target": "components/spectrumui/blocks/tables/leaderboard-table.tsx"
    }
  ],
  "type": "registry:block"
}
