DataTable
datatable-1779378817400.tsx
'use client'
import { useState, useMemo } from 'react'
const ROWS = Array.from({ length: 32 }, (_, i) => ({
id: i + 1,
name: ['Aurora Dashboard', 'Nexus Portal', 'Helix Analytics', 'Vertex CRM', 'Pulse Monitor', 'Orbit Tracker', 'Zenith Suite', 'Apex Workflow'][i % 8] + (i >= 8 ? ' Pro' : ''),
status: (['active', 'paused', 'draft', 'archived'] as const)[i % 4],
users: Math.floor(Math.random() * 10000 + 100),
revenue: Math.floor(Math.random() * 50000 + 1000),
updated: `${2024 + (i % 2)}-${String((i % 12) + 1).padStart(2,'0')}-${String((i % 28) + 1).padStart(2,'0')}`,
}))
const STATUS_COLORS = { active: '#22c55e', paused: '#f59e0b', draft: '#6366f1', archived: '#64748b' }
type SortKey = 'name' | 'users' | 'revenue' | 'updated'
export default function DataTable() {
const [sort, setSort] = useState<{ key: SortKey; dir: 1 | -1 }>({ key: 'revenue', dir: -1 })
const [page, setPage] = useState(0)
const [filter, setFilter] = useState('')
const [selected, setSelected] = useState<Set<number>>(new Set())
const PER = 8
const rows = useMemo(() => {
let r = ROWS.filter(row => row.name.toLowerCase().includes(filter.toLowerCase()))
r.sort((a, b) => {
const av = a[sort.key], bv = b[sort.key]
return typeof av === 'number' ? (av - (bv as number)) * sort.dir : String(av).localeCompare(String(bv)) * sort.dir
})
return r
}, [sort, filter])
const page_rows = rows.slice(page * PER, (page + 1) * PER)
const total_pages = Math.ceil(rows.length / PER)
function toggleSort(key: SortKey) {
setSort(s => s.key === key ? { key, dir: s.dir === 1 ? -1 : 1 } : { key, dir: -1 })
setPage(0)
}
const SortIcon = ({ k }: { k: SortKey }) => (
<span style={{ marginLeft: 4, opacity: sort.key === k ? 1 : 0.3, fontSize: 10 }}>{sort.key === k ? (sort.dir === 1 ? '▲' : '▼') : '▼'}</span>
)
return (
<div style={{ background: '#0D0D0D', border: '1px solid rgba(255,255,255,0.06)', borderRadius: 16, overflow: 'hidden' }}>
<div style={{ padding: '16px 20px', borderBottom: '1px solid rgba(255,255,255,0.06)', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<div style={{ color: '#F5F5F0', fontSize: 15, fontWeight: 600 }}>Projects {selected.size > 0 && <span style={{ color: '#C9A84C', fontSize: 13, marginLeft: 8 }}>{selected.size} selected</span>}</div>
<input placeholder="Search..." value={filter} onChange={e => { setFilter(e.target.value); setPage(0) }}
style={{ background: 'rgba(255,255,255,0.05)', border: '1px solid rgba(255,255,255,0.1)', borderRadius: 8, color: '#F5F5F0', padding: '7px 12px', fontSize: 13, outline: 'none', width: 180 }} />
</div>
<div style={{ overflowX: 'auto' }}>
<table style={{ width: '100%', borderCollapse: 'collapse', fontSize: 13 }}>
<thead>
<tr style={{ borderBottom: '1px solid rgba(255,255,255,0.06)' }}>
<th style={th}><input type="checkbox" onChange={e => setSelected(e.target.checked ? new Set(page_rows.map(r => r.id)) : new Set())} /></th>
{(['name', 'status', 'users', 'revenue', 'updated'] as const).map(k => (
<th key={k} onClick={() => k !== 'status' && toggleSort(k as SortKey)} style={{ ...th, cursor: k !== 'status' ? 'pointer' : 'default', textAlign: k === 'users' || k === 'revenue' ? 'right' : 'left' }}>
{k.charAt(0).toUpperCase() + k.slice(1)}{k !== 'status' && <SortIcon k={k as SortKey} />}
</th>
))}
</tr>
</thead>
<tbody>
{page_rows.map(row => (
<tr key={row.id} style={{ borderBottom: '1px solid rgba(255,255,255,0.04)', background: selected.has(row.id) ? 'rgba(201,168,76,0.05)' : 'transparent' }}>
<td style={td}><input type="checkbox" checked={selected.has(row.id)} onChange={e => setSelected(s => { const n = new Set(s); e.target.checked ? n.add(row.id) : n.delete(row.id); return n })} /></td>
<td style={td}><span style={{ color: '#F5F5F0', fontWeight: 500 }}>{row.name}</span></td>
<td style={td}><span style={{ background: STATUS_COLORS[row.status] + '20', color: STATUS_COLORS[row.status], padding: '2px 8px', borderRadius: 20, fontSize: 11, fontWeight: 600 }}>{row.status}</span></td>
<td style={{ ...td, textAlign: 'right', color: 'rgba(255,255,255,0.7)' }}>{row.users.toLocaleString()}</td>
<td style={{ ...td, textAlign: 'right', color: '#C9A84C', fontWeight: 600 }}>${row.revenue.toLocaleString()}</td>
<td style={{ ...td, color: 'rgba(255,255,255,0.4)' }}>{row.updated}</td>
</tr>
))}
</tbody>
</table>
</div>
<div style={{ padding: '12px 20px', borderTop: '1px solid rgba(255,255,255,0.06)', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<span style={{ color: 'rgba(255,255,255,0.4)', fontSize: 12 }}>{rows.length} results</span>
<div style={{ display: 'flex', gap: 4 }}>
{Array.from({ length: total_pages }, (_, i) => (
<button key={i} onClick={() => setPage(i)} style={{ width: 28, height: 28, borderRadius: 6, border: '1px solid rgba(255,255,255,0.1)', background: page === i ? '#C9A84C' : 'transparent', color: page === i ? '#0A0A0A' : 'rgba(255,255,255,0.5)', cursor: 'pointer', fontSize: 12, fontWeight: 600 }}>{i + 1}</button>
))}
</div>
</div>
</div>
)
}
const th: React.CSSProperties = { padding: '10px 12px', color: 'rgba(255,255,255,0.4)', fontWeight: 600, textAlign: 'left', fontSize: 11, userSelect: 'none', textTransform: 'uppercase', letterSpacing: '0.05em' }
const td: React.CSSProperties = { padding: '11px 12px', color: 'rgba(255,255,255,0.7)' }Component info
CategoryTables
Frameworkreact
TierFREE
Views0
Copies0
About
Sortable data table with pagination, column filters, row selection, and status badges