diff --git a/frontend/components/table/ColumnHeader.tsx b/frontend/components/table/ColumnHeader.tsx
index 019905c..4ba9361 100644
--- a/frontend/components/table/ColumnHeader.tsx
+++ b/frontend/components/table/ColumnHeader.tsx
@@ -5,6 +5,51 @@ import type { DatasetRow, DatasetColumn } from "./types";
import { ColumnIcon } from "./ColumnIcon";
import { floorWidth } from "./utils";
+function SortIndicator({ direction }: { direction: false | "asc" | "desc" }) {
+ if (direction === "asc") {
+ return (
+
+ );
+ }
+ if (direction === "desc") {
+ return (
+
+ );
+ }
+ // Unsorted: show a faint up/down chevron pair as a hint that the column is sortable
+ return (
+
+ );
+}
+
export function ColumnHeader({
header,
column,
@@ -16,6 +61,10 @@ export function ColumnHeader({
isResizing: boolean;
containerHeight: number;
}) {
+ const isSorted = header.column.getIsSorted();
+ const canSort = header.column.getCanSort();
+ const toggleSort = header.column.getToggleSortingHandler();
+
return (
{
+ if (e.key === "Enter" || e.key === " ") {
+ e.preventDefault();
+ header.column.toggleSorting();
+ }
+ }
+ : undefined
+ }
+ aria-sort={
+ isSorted === "asc"
+ ? "ascending"
+ : isSorted === "desc"
+ ? "descending"
+ : undefined
+ }
>
{column && }
{column?.isPrimaryKey && (
@@ -45,6 +118,7 @@ export function ColumnHeader({
)}
{column?.name ?? header.id}
+ {canSort && }
{
+ const a = rowA.getValue(columnId);
+ const b = rowB.getValue(columnId);
+ const toNum = (v: unknown): number => {
+ if (typeof v === "number") return v;
+ if (typeof v !== "string") return Number.NaN;
+ const n = Number(v.replace(/[^0-9.-]/g, ""));
+ return Number.isFinite(n) ? n : Number.NaN;
+ };
+ const na = toNum(a);
+ const nb = toNum(b);
+ if (!Number.isNaN(na) && !Number.isNaN(nb)) return na - nb;
+ return String(a ?? "").localeCompare(String(b ?? ""), undefined, {
+ sensitivity: "base",
+ });
+ },
}),
);
@@ -84,6 +106,8 @@ export function DatasetTable({
return () => observer.disconnect();
}, []);
+ const [sorting, setSorting] = useState([]);
+
const [storedWidths, setStoredWidths] = usePersistedColumnWidths(datasetId);
const columns = useMemo(
@@ -96,6 +120,9 @@ export function DatasetTable({
columns,
columnResizeMode: "onChange",
getCoreRowModel: getCoreRowModel(),
+ getSortedRowModel: getSortedRowModel(),
+ onSortingChange: setSorting,
+ state: { sorting },
getRowId: (row) => row._id,
});