Compare commits

...

1 Commits

Author SHA1 Message Date
hsiegeln
039f2fa5fe feat: add onSortChange callback to DataTable for server-side sorting
All checks were successful
Build & Publish / publish (push) Successful in 1m21s
When onSortChange is provided, DataTable operates in controlled mode:
it renders sort indicators and fires the callback on header clicks,
but skips client-side sorting — the caller provides pre-sorted data.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 16:51:22 +01:00
2 changed files with 14 additions and 3 deletions

View File

@@ -24,6 +24,7 @@ export function DataTable<T extends { id: string }>({
rowAccent,
expandedContent,
flush = false,
onSortChange,
}: DataTableProps<T>) {
const [sortKey, setSortKey] = useState<string | null>(null)
const [sortDir, setSortDir] = useState<SortDir>('asc')
@@ -31,14 +32,16 @@ export function DataTable<T extends { id: string }>({
const [pageSize, setPageSize] = useState(initialPageSize)
const [expandedId, setExpandedId] = useState<string | null>(null)
// When onSortChange is provided (controlled mode), skip client-side sorting
const sorted = useMemo(() => {
if (onSortChange) return data
if (!sortKey) return data
return [...data].sort((a, b) => {
const av = (a as Record<string, unknown>)[sortKey]
const bv = (b as Record<string, unknown>)[sortKey]
return compareValues(av, bv, sortDir)
})
}, [data, sortKey, sortDir])
}, [data, sortKey, sortDir, onSortChange])
const totalRows = sorted.length
const totalPages = Math.max(1, Math.ceil(totalRows / pageSize))
@@ -52,13 +55,17 @@ export function DataTable<T extends { id: string }>({
function handleHeaderClick(col: Column<T>) {
if (!sortable && !col.sortable) return
let newDir: SortDir
if (sortKey === col.key) {
setSortDir((d) => (d === 'asc' ? 'desc' : 'asc'))
newDir = sortDir === 'asc' ? 'desc' : 'asc'
setSortDir(newDir)
} else {
newDir = 'asc'
setSortKey(col.key)
setSortDir('asc')
setSortDir(newDir)
}
setPage(1)
onSortChange?.(col.key, newDir)
}
function handleRowClick(row: T) {

View File

@@ -20,4 +20,8 @@ export interface DataTableProps<T extends { id: string }> {
expandedContent?: (row: T) => ReactNode | null
/** Strip border, radius, and shadow so the table sits flush inside a parent container. */
flush?: boolean
/** Controlled sort: called when the user clicks a sortable column header.
* When provided, the component skips client-side sorting — the caller is
* responsible for providing `data` in the desired order. */
onSortChange?: (key: string, dir: 'asc' | 'desc') => void
}