import type { Column, TDisplayFunction } from './types.js'; export function computeColumnsFromDisplayFunction( displayFunction: TDisplayFunction, data: T[] ): Column[] { if (!data || data.length === 0) return []; const firstTransformedItem = displayFunction(data[0]); const keys: string[] = Object.keys(firstTransformedItem); return keys.map((key) => ({ key, header: key, value: (row: T) => displayFunction(row)[key], })); } export function computeEffectiveColumns( columns: Column[] | undefined, augmentFromDisplayFunction: boolean, displayFunction: TDisplayFunction, data: T[] ): Column[] { const base = (columns || []).slice(); if (!augmentFromDisplayFunction) return base; const fromDisplay = computeColumnsFromDisplayFunction(displayFunction, data); const existingKeys = new Set(base.map((c) => String(c.key))); for (const col of fromDisplay) { if (!existingKeys.has(String(col.key))) { base.push(col); } } return base; } export function getCellValue(row: T, col: Column, displayFunction?: TDisplayFunction): any { return col.value ? col.value(row) : (row as any)[col.key as any]; } export function getViewData( data: T[], effectiveColumns: Column[], sortKey?: string, sortDir?: 'asc' | 'desc' | null, filterText?: string, columnFilters?: Record ): T[] { let arr = data.slice(); const ft = (filterText || '').trim().toLowerCase(); const cf = columnFilters || {}; const cfKeys = Object.keys(cf).filter((k) => (cf[k] ?? '').trim().length > 0); if (ft || cfKeys.length > 0) { arr = arr.filter((row) => { // column filters (AND across columns) for (const k of cfKeys) { const col = effectiveColumns.find((c) => String(c.key) === k); if (!col || col.hidden || col.filterable === false) continue; const val = getCellValue(row, col); const s = String(val ?? '').toLowerCase(); const needle = String(cf[k]).toLowerCase(); if (!s.includes(needle)) return false; } // global filter (OR across visible columns) if (ft) { let any = false; for (const col of effectiveColumns) { if (col.hidden) continue; const val = getCellValue(row, col); const s = String(val ?? '').toLowerCase(); if (s.includes(ft)) { any = true; break; } } if (!any) return false; } 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; 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; return 0; }); return arr; }