mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-20 13:11:12 -05:00
102 lines
2.6 KiB
TypeScript
102 lines
2.6 KiB
TypeScript
/**
|
|
* Optimized List Hook
|
|
* Provides memoized filtering, sorting, and pagination for large lists
|
|
*/
|
|
|
|
import { useMemo } from 'react';
|
|
|
|
export interface UseOptimizedListOptions<T> {
|
|
items: T[];
|
|
searchTerm?: string;
|
|
searchFields?: (keyof T)[];
|
|
sortField?: keyof T;
|
|
sortDirection?: 'asc' | 'desc';
|
|
pageSize?: number;
|
|
currentPage?: number;
|
|
}
|
|
|
|
export interface UseOptimizedListResult<T> {
|
|
filteredItems: T[];
|
|
paginatedItems: T[];
|
|
totalCount: number;
|
|
pageCount: number;
|
|
}
|
|
|
|
export function useOptimizedList<T extends Record<string, any>>({
|
|
items,
|
|
searchTerm = '',
|
|
searchFields = [],
|
|
sortField,
|
|
sortDirection = 'asc',
|
|
pageSize,
|
|
currentPage = 1,
|
|
}: UseOptimizedListOptions<T>): UseOptimizedListResult<T> {
|
|
// Memoized filtering
|
|
const filteredItems = useMemo(() => {
|
|
if (!searchTerm || searchFields.length === 0) {
|
|
return items;
|
|
}
|
|
|
|
const lowerSearchTerm = searchTerm.toLowerCase();
|
|
return items.filter(item =>
|
|
searchFields.some(field => {
|
|
const value = item[field];
|
|
if (value == null) return false;
|
|
return String(value).toLowerCase().includes(lowerSearchTerm);
|
|
})
|
|
);
|
|
}, [items, searchTerm, searchFields]);
|
|
|
|
// Memoized sorting
|
|
const sortedItems = useMemo(() => {
|
|
if (!sortField) {
|
|
return filteredItems;
|
|
}
|
|
|
|
return [...filteredItems].sort((a, b) => {
|
|
const aValue = a[sortField];
|
|
const bValue = b[sortField];
|
|
|
|
if (aValue == null && bValue == null) return 0;
|
|
if (aValue == null) return sortDirection === 'asc' ? 1 : -1;
|
|
if (bValue == null) return sortDirection === 'asc' ? -1 : 1;
|
|
|
|
if (typeof aValue === 'string' && typeof bValue === 'string') {
|
|
return sortDirection === 'asc'
|
|
? aValue.localeCompare(bValue)
|
|
: bValue.localeCompare(aValue);
|
|
}
|
|
|
|
if (typeof aValue === 'number' && typeof bValue === 'number') {
|
|
return sortDirection === 'asc' ? aValue - bValue : bValue - aValue;
|
|
}
|
|
|
|
return 0;
|
|
});
|
|
}, [filteredItems, sortField, sortDirection]);
|
|
|
|
// Memoized pagination
|
|
const paginatedItems = useMemo(() => {
|
|
if (!pageSize) {
|
|
return sortedItems;
|
|
}
|
|
|
|
const startIndex = (currentPage - 1) * pageSize;
|
|
const endIndex = startIndex + pageSize;
|
|
return sortedItems.slice(startIndex, endIndex);
|
|
}, [sortedItems, pageSize, currentPage]);
|
|
|
|
// Calculate page count
|
|
const pageCount = useMemo(() => {
|
|
if (!pageSize) return 1;
|
|
return Math.ceil(sortedItems.length / pageSize);
|
|
}, [sortedItems.length, pageSize]);
|
|
|
|
return {
|
|
filteredItems: sortedItems,
|
|
paginatedItems,
|
|
totalCount: sortedItems.length,
|
|
pageCount,
|
|
};
|
|
}
|