Skip to content

Sorting

WitTable supports single and multi-column sorting through the SortEngine. Click a column header to cycle through sort states: none → ascending → descending → none. Hold Shift while clicking to add secondary sort columns.

Sorting is type-aware via CellTypeRegistry. Cross-type comparison order: number < boolean < string. Null values are always sorted last regardless of direction.

Live Demo
Click headers to sort. Hold Shift and click another header for multi-column sort.
Click a column header to sort
View source code
SortingDemo.tsx
import { useState, useRef } from 'react';
import { WitTable } from '@witqq/spreadsheet-react';
import type { WitTableRef } from '@witqq/spreadsheet-react';
import type { SortChangeEvent } from '@witqq/spreadsheet';
import { DemoWrapper } from './DemoWrapper';
import { generateEmployees, employeeColumns } from './generate-data';
import { useSiteTheme } from './useSiteTheme';
const data = generateEmployees(100);
const sortableColumns = employeeColumns.map(col => ({ ...col, sortable: true }));
export function SortingDemo() {
const { witTheme } = useSiteTheme();
const [sortInfo, setSortInfo] = useState('Click a column header to sort');
const tableRef = useRef<WitTableRef>(null);
const handleSortChange = (event: SortChangeEvent) => {
const cols = event.sortColumns;
if (cols.length === 0) {
setSortInfo('No sort applied');
} else {
const desc = cols.map(s => `${sortableColumns[s.col]?.title ?? `col ${s.col}`} (${s.direction})`).join(', ');
setSortInfo(`Sorted by: ${desc}`);
}
};
return (
<DemoWrapper title="Live Demo" description="Click headers to sort. Hold Shift and click another header for multi-column sort." height={440}>
<div style={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
<div style={{ padding: '0.5rem 0.75rem', fontSize: '0.8rem', color: '#64748b', borderBottom: '1px solid #e2e8f0', flexShrink: 0 }}>
{sortInfo}
</div>
<div style={{ flex: 1 }}>
<WitTable
theme={witTheme}
ref={tableRef}
columns={sortableColumns}
data={data}
showRowNumbers
editable={false}
sortable
onSortChange={handleSortChange}
style={{ width: '100%', height: '100%' }}
/>
</div>
</div>
</DemoWrapper>
);
}

Enable sorting globally or per-column:

import { WitTable } from '@witqq/spreadsheet-react';
const columns: ColumnDef[] = [
{ key: 'name', title: 'Name', sortable: true },
{ key: 'age', title: 'Age', type: 'number', sortable: true },
{ key: 'id', title: 'ID', sortable: false },
];
function App() {
return (
<WitTable
columns={columns}
data={data}
sortable={true}
onSortChange={(sortColumns) => {
console.log('Sort changed:', sortColumns);
}}
/>
);
}

Control sort state directly through the SortEngine:

const ref = useRef<WitTableRef>(null);
// Set multi-column sort
ref.current?.getInstance().getSortEngine().setSortColumns([
{ col: 1, direction: 'asc' },
{ col: 3, direction: 'desc' },
]);
// Toggle a single column
ref.current?.getInstance().getSortEngine().toggleColumn(2);
// Clear all sorting
ref.current?.getInstance().getSortEngine().clearSort();
// Read current sort state
const cols = ref.current?.getInstance().getSortEngine().sortColumns;
MethodSignatureDescription
toggleColumn(col: number) => SortColumn[]Cycle sort on column (none→asc→desc→none)
setSortColumns(columns: SortColumn[]) => voidSet full sort state
clearSort() => voidRemove all sorting
sortColumnsreadonly SortColumn[]Current sort columns (getter)
interface SortColumn {
col: number;
direction: 'asc' | 'desc';
}

The low-level comparison function used by SortEngine:

import { compareCellValues } from '@witqq/spreadsheet';
// Type-aware: number < boolean < string, nulls always last
compareCellValues(valA, valB, 'asc'); // -1 | 0 | 1
  • Filtering — filters applied before sort via DataView pipeline
  • DataView — logical↔physical row mapping