feat(dees-table): add multi-column sorting with header menu controls and priority indicators
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import type { Column, TDisplayFunction } from './types.js';
|
||||
import type { Column, ISortDescriptor, TDisplayFunction } from './types.js';
|
||||
|
||||
export function computeColumnsFromDisplayFunction<T>(
|
||||
displayFunction: TDisplayFunction<T>,
|
||||
@@ -36,11 +36,31 @@ export function getCellValue<T>(row: T, col: Column<T>, displayFunction?: TDispl
|
||||
return col.value ? col.value(row) : (row as any)[col.key as any];
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares two cell values in ascending order. Returns -1, 0, or 1.
|
||||
* Null/undefined values sort before defined values. Numbers compare numerically;
|
||||
* everything else compares as case-insensitive strings.
|
||||
*/
|
||||
export function compareCellValues(va: any, vb: any): number {
|
||||
if (va == null && vb == null) return 0;
|
||||
if (va == null) return -1;
|
||||
if (vb == null) return 1;
|
||||
if (typeof va === 'number' && typeof vb === 'number') {
|
||||
if (va < vb) return -1;
|
||||
if (va > vb) return 1;
|
||||
return 0;
|
||||
}
|
||||
const sa = String(va).toLowerCase();
|
||||
const sb = String(vb).toLowerCase();
|
||||
if (sa < sb) return -1;
|
||||
if (sa > sb) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
export function getViewData<T>(
|
||||
data: T[],
|
||||
effectiveColumns: Column<T>[],
|
||||
sortKey?: string,
|
||||
sortDir?: 'asc' | 'desc' | null,
|
||||
sortBy: ISortDescriptor[],
|
||||
filterText?: string,
|
||||
columnFilters?: Record<string, string>,
|
||||
filterMode: 'table' | 'data' = 'table',
|
||||
@@ -94,21 +114,17 @@ export function getViewData<T>(
|
||||
return true;
|
||||
});
|
||||
}
|
||||
if (!sortKey || !sortDir) return arr;
|
||||
const col = effectiveColumns.find((c) => String(c.key) === sortKey);
|
||||
if (!col) return arr;
|
||||
const dir = sortDir === 'asc' ? 1 : -1;
|
||||
if (!sortBy || sortBy.length === 0) return arr;
|
||||
// Pre-resolve descriptors -> columns once for performance.
|
||||
const resolved = sortBy
|
||||
.map((desc) => ({ desc, col: effectiveColumns.find((c) => String(c.key) === desc.key) }))
|
||||
.filter((entry): entry is { desc: ISortDescriptor; col: Column<T> } => !!entry.col);
|
||||
if (resolved.length === 0) return arr;
|
||||
arr.sort((a, b) => {
|
||||
const va = getCellValue(a, col);
|
||||
const vb = getCellValue(b, col);
|
||||
if (va == null && vb == null) return 0;
|
||||
if (va == null) return -1 * dir;
|
||||
if (vb == null) return 1 * dir;
|
||||
if (typeof va === 'number' && typeof vb === 'number') return (va - vb) * dir;
|
||||
const sa = String(va).toLowerCase();
|
||||
const sb = String(vb).toLowerCase();
|
||||
if (sa < sb) return -1 * dir;
|
||||
if (sa > sb) return 1 * dir;
|
||||
for (const { desc, col } of resolved) {
|
||||
const cmp = compareCellValues(getCellValue(a, col), getCellValue(b, col));
|
||||
if (cmp !== 0) return desc.dir === 'asc' ? cmp : -cmp;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
return arr;
|
||||
|
||||
Reference in New Issue
Block a user