c-data-grid-29.tsx
"use client"
// This file keeps "use no memo": its own cell/header templates read state
// through builder calls on a stable row/column, which React Compiler cannot
// see. The primitive wraps its own such reads in TanStack's Subscribe; a
// consumer template has to opt out or subscribe itself.
"use no memo"
import { useMemo, useState } from "react"
import { useCopyToClipboard } from "@/hooks/use-copy-to-clipboard"
import { Badge } from "@/components/reui/badge"
import {
DataGrid,
DataGridContainer,
dataGridFeatures,
type DataGridFeatures,
} from "@/components/reui/data-grid/data-grid"
import { DataGridColumnHeader } from "@/components/reui/data-grid/data-grid-column-header"
import { DataGridPagination } from "@/components/reui/data-grid/data-grid-pagination"
import { DataGridScrollArea } from "@/components/reui/data-grid/data-grid-scroll-area"
import {
DataGridTable,
DataGridTableRowPin,
} from "@/components/reui/data-grid/data-grid-table"
import {
ColumnDef,
PaginationState,
Row,
RowPinningState,
SortingState,
useTable,
} from "@tanstack/react-table"
import { toast } from "sonner"
import {
Avatar,
AvatarFallback,
AvatarImage,
} from "@/components/ui/avatar"
import { Button } from "@/components/ui/button"
import {
Card,
CardContent,
CardFooter,
CardHeader,
} from "@/components/ui/card"
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"
import { IconPlaceholder } from "@/app/(create)/components/icon-placeholder"
interface IData {
id: string
name: string
avatar: string
role: string
status: "Active" | "Inactive" | "Pending" | "Blocked"
balance: number
}
const demoData: IData[] = [
{
id: "1",
name: "Alex Johnson",
avatar:
"https://images.unsplash.com/photo-1535713875002-d1d0cf377fde?w=96&h=96&dpr=2&q=80",
role: "CEO",
status: "Active",
balance: 5143.03,
},
{
id: "2",
name: "Sarah Chen",
avatar:
"https://images.unsplash.com/photo-1519699047748-de8e457a634e?w=96&h=96&dpr=2&q=80",
role: "CTO",
status: "Active",
balance: 4321.87,
},
{
id: "3",
name: "Michael Rodriguez",
avatar:
"https://images.unsplash.com/photo-1584308972272-9e4e7685e80f?w=96&h=96&dpr=2&q=80",
role: "Designer",
status: "Blocked",
balance: 7654.98,
},
{
id: "4",
name: "Emma Wilson",
avatar:
"https://images.unsplash.com/photo-1485893086445-ed75865251e0?w=96&h=96&dpr=2&q=80",
role: "Developer",
status: "Inactive",
balance: 3456.45,
},
{
id: "5",
name: "David Kim",
avatar:
"https://images.unsplash.com/photo-1607990281513-2c110a25bd8c?w=96&h=96&dpr=2&q=80",
role: "Lawyer",
status: "Active",
balance: 9876.54,
},
{
id: "6",
name: "Aron Thompson",
avatar:
"https://images.unsplash.com/photo-1527980965255-d3b416303d12?w=96&h=96&dpr=2&q=80",
role: "Director",
status: "Pending",
balance: 6214.22,
},
{
id: "7",
name: "James Brown",
avatar:
"https://images.unsplash.com/photo-1543299750-19d1d6297053?w=96&h=96&dpr=2&q=80",
role: "Product Manager",
status: "Inactive",
balance: 5321.77,
},
{
id: "8",
name: "Maria Garcia",
avatar:
"https://images.unsplash.com/photo-1620075225255-8c2051b6c015?w=96&h=96&dpr=2&q=80",
role: "Marketing Lead",
status: "Active",
balance: 8452.39,
},
{
id: "9",
name: "Nick Johnson",
avatar:
"https://images.unsplash.com/photo-1485206412256-701ccc5b93ca?w=96&h=96&dpr=2&q=80",
role: "Data Scientist",
status: "Pending",
balance: 7345.1,
},
{
id: "10",
name: "Liam Thompson",
avatar:
"https://images.unsplash.com/photo-1542595913-85d69b0edbaf?w=96&h=96&dpr=2&q=80",
role: "Engineer",
status: "Inactive",
balance: 5214.88,
},
{
id: "11",
name: "Olivia Martinez",
avatar:
"https://images.unsplash.com/photo-1534528741775-53994a69daeb?w=96&h=96&dpr=2&q=80",
role: "VP of Sales",
status: "Active",
balance: 11234.56,
},
{
id: "12",
name: "Ethan Park",
avatar:
"https://images.unsplash.com/photo-1506794778202-cad84cf45f1d?w=96&h=96&dpr=2&q=80",
role: "DevOps Lead",
status: "Active",
balance: 8910.33,
},
{
id: "13",
name: "Sophie Taylor",
avatar:
"https://images.unsplash.com/photo-1544005313-94ddf0286df2?w=96&h=96&dpr=2&q=80",
role: "UX Researcher",
status: "Pending",
balance: 6543.21,
},
{
id: "14",
name: "Ryan Mitchell",
avatar:
"https://images.unsplash.com/photo-1500648767791-00dcc994a43e?w=96&h=96&dpr=2&q=80",
role: "Architect",
status: "Inactive",
balance: 7821.44,
},
{
id: "15",
name: "Isabella Wong",
avatar:
"https://images.unsplash.com/photo-1438761681033-6461ffad8d80?w=96&h=96&dpr=2&q=80",
role: "QA Manager",
status: "Active",
balance: 5678.9,
},
]
function ActionsCell({ row }: { row: Row<DataGridFeatures, IData> }) {
const { copyToClipboard } = useCopyToClipboard()
const isPinned = row.getIsPinned()
const handleCopyId = () => {
copyToClipboard(row.original.id)
toast.success("Employee ID copied", { description: row.original.id })
}
return (
<DropdownMenu>
<DropdownMenuTrigger
render={
<Button className="size-7" size="icon" variant="ghost">
<IconPlaceholder
lucide="MoreHorizontalIcon"
tabler="IconDots"
hugeicons="MoreHorizontalCircle01Icon"
phosphor="DotsThreeIcon"
remixicon="RiMoreLine"
/>
</Button>
}
/>
<DropdownMenuContent side="bottom" align="start">
<DropdownMenuItem onClick={() => row.pin(isPinned ? false : "top")}>
{isPinned ? "Unpin row" : "Pin to top"}
</DropdownMenuItem>
<DropdownMenuItem onClick={() => {}}>Edit</DropdownMenuItem>
<DropdownMenuItem onClick={handleCopyId}>Copy ID</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem variant="destructive" onClick={() => {}}>
Delete
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
)
}
export function Pattern() {
const [pagination, setPagination] = useState<PaginationState>({
pageIndex: 0,
pageSize: 10,
})
const [sorting, setSorting] = useState<SortingState>([
{ id: "name", desc: false },
])
const [rowPinning, setRowPinning] = useState<RowPinningState>({
top: [],
bottom: [],
})
const columns = useMemo<ColumnDef<DataGridFeatures, IData>[]>(
() => [
{
id: "pin",
header: "",
cell: ({ row }) => <DataGridTableRowPin row={row} />,
size: 40,
enableSorting: false,
enableHiding: false,
enableResizing: false,
},
{
accessorKey: "name",
id: "name",
header: ({ column }) => (
<DataGridColumnHeader title="User" column={column} />
),
cell: ({ row }) => (
<div className="flex items-center gap-3">
<Avatar className="size-8">
<AvatarImage src={row.original.avatar} alt={row.original.name} />
<AvatarFallback>
{row.original.name
.split(" ")
.map((n) => n[0])
.join("")}
</AvatarFallback>
</Avatar>
<div className="text-foreground min-w-0 truncate font-medium">
{row.original.name}
</div>
</div>
),
minSize: 150,
meta: {
autoSize: true,
},
enableSorting: true,
enableHiding: false,
},
{
accessorKey: "role",
id: "role",
header: ({ column }) => (
<DataGridColumnHeader title="Role" column={column} />
),
cell: ({ row }) => (
<span className="text-foreground">{row.original.role}</span>
),
size: 160,
enableSorting: true,
},
{
accessorKey: "status",
id: "status",
header: ({ column }) => (
<DataGridColumnHeader title="Status" column={column} />
),
cell: ({ row }) => {
const s = row.original.status
if (s === "Active")
return <Badge variant="success-outline">Active</Badge>
if (s === "Blocked")
return <Badge variant="destructive-outline">Blocked</Badge>
if (s === "Inactive")
return <Badge variant="info-outline">Inactive</Badge>
return <Badge variant="warning-outline">Pending</Badge>
},
size: 120,
enableSorting: true,
},
{
accessorKey: "balance",
id: "balance",
header: ({ column }) => (
<DataGridColumnHeader title="Balance" column={column} />
),
cell: ({ row }) => (
<span className="text-foreground tabular-nums">
$
{row.original.balance.toLocaleString("en-US", {
minimumFractionDigits: 2,
})}
</span>
),
size: 140,
enableSorting: true,
},
{
id: "actions",
header: "",
cell: ({ row }) => <ActionsCell row={row} />,
size: 60,
enableSorting: false,
enableHiding: false,
enableResizing: false,
},
],
[]
)
const table = useTable({
features: dataGridFeatures,
columns,
data: demoData,
pageCount: Math.ceil(demoData.length / pagination.pageSize),
getRowId: (row: IData) => row.id,
enableRowPinning: true,
keepPinnedRows: true,
state: { pagination, sorting, rowPinning },
onPaginationChange: setPagination,
onSortingChange: setSorting,
onRowPinningChange: setRowPinning,
})
return (
<DataGrid
table={table}
recordCount={demoData.length}
tableLayout={{
rowsPinnable: true,
columnsResizable: true,
}}
>
<Card className="w-full gap-0 p-0">
<CardHeader className="flex items-center justify-between px-3 py-2">
<div className="flex items-center gap-2">
<span className="text-foreground text-sm font-medium">
Team Members
</span>
{(rowPinning.top?.length ?? 0) > 0 && (
<Badge variant="primary-outline" size="sm">
{rowPinning.top?.length} pinned
</Badge>
)}
</div>
<div className="flex items-center gap-1">
{(rowPinning.top?.length ?? 0) > 0 && (
<Button
variant="ghost"
size="sm"
onClick={() => setRowPinning({ top: [], bottom: [] })}
>
Unpin all
</Button>
)}
<Button variant="ghost" size="icon" className="size-8">
<IconPlaceholder
lucide="RefreshCwIcon"
tabler="IconRefresh"
hugeicons="RefreshIcon"
phosphor="ArrowClockwiseIcon"
remixicon="RiRefreshLine"
className="size-4"
/>
</Button>
</div>
</CardHeader>
<CardContent className="p-0">
<Card className="p-0">
<DataGridContainer>
<DataGridScrollArea>
<DataGridTable />
</DataGridScrollArea>
</DataGridContainer>
</Card>
</CardContent>
<CardFooter className="border-none bg-transparent! px-3 py-2">
<DataGridPagination />
</CardFooter>
</Card>
</DataGrid>
)
}





























