| 
									
										
										
										
											2025-09-16 14:53:59 +00:00
										 |  |  | import type { Column, TDisplayFunction } from './types.js'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export function computeColumnsFromDisplayFunction<T>( | 
					
						
							|  |  |  |   displayFunction: TDisplayFunction<T>, | 
					
						
							|  |  |  |   data: T[] | 
					
						
							|  |  |  | ): Column<T>[] { | 
					
						
							|  |  |  |   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<T>( | 
					
						
							|  |  |  |   columns: Column<T>[] | undefined, | 
					
						
							|  |  |  |   augmentFromDisplayFunction: boolean, | 
					
						
							|  |  |  |   displayFunction: TDisplayFunction<T>, | 
					
						
							|  |  |  |   data: T[] | 
					
						
							|  |  |  | ): Column<T>[] { | 
					
						
							|  |  |  |   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<T>(row: T, col: Column<T>, displayFunction?: TDisplayFunction<T>): any { | 
					
						
							|  |  |  |   return col.value ? col.value(row) : (row as any)[col.key as any]; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export function getViewData<T>( | 
					
						
							|  |  |  |   data: T[], | 
					
						
							|  |  |  |   effectiveColumns: Column<T>[], | 
					
						
							|  |  |  |   sortKey?: string, | 
					
						
							|  |  |  |   sortDir?: 'asc' | 'desc' | null, | 
					
						
							| 
									
										
										
										
											2025-09-16 15:17:33 +00:00
										 |  |  |   filterText?: string, | 
					
						
							| 
									
										
										
										
											2025-09-16 15:46:44 +00:00
										 |  |  |   columnFilters?: Record<string, string>, | 
					
						
							|  |  |  |   filterMode: 'table' | 'data' = 'table', | 
					
						
							|  |  |  |   lucenePredicate?: (row: T) => boolean | 
					
						
							| 
									
										
										
										
											2025-09-16 14:53:59 +00:00
										 |  |  | ): T[] { | 
					
						
							|  |  |  |   let arr = data.slice(); | 
					
						
							|  |  |  |   const ft = (filterText || '').trim().toLowerCase(); | 
					
						
							| 
									
										
										
										
											2025-09-16 15:17:33 +00:00
										 |  |  |   const cf = columnFilters || {}; | 
					
						
							|  |  |  |   const cfKeys = Object.keys(cf).filter((k) => (cf[k] ?? '').trim().length > 0); | 
					
						
							|  |  |  |   if (ft || cfKeys.length > 0) { | 
					
						
							| 
									
										
										
										
											2025-09-16 14:53:59 +00:00
										 |  |  |     arr = arr.filter((row) => { | 
					
						
							| 
									
										
										
										
											2025-09-16 15:17:33 +00:00
										 |  |  |       // column filters (AND across columns)
 | 
					
						
							|  |  |  |       for (const k of cfKeys) { | 
					
						
							| 
									
										
										
										
											2025-09-16 15:46:44 +00:00
										 |  |  |         if (filterMode === 'data') { | 
					
						
							|  |  |  |           // raw object check for that key
 | 
					
						
							|  |  |  |           const val = (row as any)[k]; | 
					
						
							|  |  |  |           const s = String(val ?? '').toLowerCase(); | 
					
						
							|  |  |  |           const needle = String(cf[k]).toLowerCase(); | 
					
						
							|  |  |  |           if (!s.includes(needle)) return false; | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |           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; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2025-09-16 14:53:59 +00:00
										 |  |  |       } | 
					
						
							| 
									
										
										
										
											2025-09-16 15:46:44 +00:00
										 |  |  |       // global filter (OR across visible columns) or lucene predicate
 | 
					
						
							| 
									
										
										
										
											2025-09-16 15:17:33 +00:00
										 |  |  |       if (ft) { | 
					
						
							| 
									
										
										
										
											2025-09-16 15:46:44 +00:00
										 |  |  |         if (lucenePredicate) { | 
					
						
							|  |  |  |           if (!lucenePredicate(row)) return false; | 
					
						
							|  |  |  |           return true; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2025-09-16 15:17:33 +00:00
										 |  |  |         let any = false; | 
					
						
							| 
									
										
										
										
											2025-09-16 15:46:44 +00:00
										 |  |  |         if (filterMode === 'data') { | 
					
						
							|  |  |  |           for (const val of Object.values(row as any)) { | 
					
						
							|  |  |  |             const s = String(val ?? '').toLowerCase(); | 
					
						
							|  |  |  |             if (s.includes(ft)) { any = true; break; } | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |           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; } | 
					
						
							| 
									
										
										
										
											2025-09-16 15:17:33 +00:00
										 |  |  |           } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if (!any) return false; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       return true; | 
					
						
							| 
									
										
										
										
											2025-09-16 14:53:59 +00:00
										 |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   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; | 
					
						
							|  |  |  | } |