bharat_erp/lib/core/utils/table_search.dart

64 lines
1.8 KiB
Dart

import 'dart:async';
/// Shared helpers for case-insensitive, trimmed, multi-field table search.
class TableSearch {
TableSearch._();
/// Trims leading/trailing whitespace. Does not alter casing.
static String normalize(String? query) => query?.trim() ?? '';
/// Trimmed + lowercased query for matching.
static String normalizedQuery(String? query) => normalize(query).toLowerCase();
/// Returns true when [query] is empty or any [values] contain the query.
static bool matches(String? query, Iterable<Object?> values) {
final q = normalizedQuery(query);
if (q.isEmpty) return true;
for (final value in values) {
if (value == null) continue;
final text = value.toString().trim().toLowerCase();
if (text.isEmpty) continue;
if (text.contains(q)) return true;
}
return false;
}
/// Filters [items] to those where any extracted field matches [query].
static List<T> filter<T>(
Iterable<T> items,
String? query,
Iterable<Object?> Function(T item) valuesOf,
) {
final q = normalizedQuery(query);
if (q.isEmpty) {
return items is List<T> ? items : items.toList();
}
return items.where((item) => matches(q, valuesOf(item))).toList();
}
}
/// Debounces search input so API-backed lists are not hit on every keystroke.
class SearchDebouncer {
SearchDebouncer({this.duration = const Duration(milliseconds: 300)});
final Duration duration;
Timer? _timer;
/// Runs [action] after [duration]. Empty queries run immediately.
void run(String query, void Function(String normalized) action) {
final normalized = TableSearch.normalize(query);
_timer?.cancel();
if (normalized.isEmpty) {
action(normalized);
return;
}
_timer = Timer(duration, () => action(normalized));
}
void dispose() {
_timer?.cancel();
_timer = null;
}
}