- Extraer drawing.zig: drawHeader, drawScrollbar, drawEditingOverlay - Extraer input.zig: handleRowClicks, handleKeyboard, handleEditingKeyboard - Extraer helpers.zig: commitEdit, parseValue, detectCRUDAction, invokeCallbacks - Extraer sorting.zig: sortRows, swapRowStates, startsWithIgnoreCase - Reducir advanced_table.zig de 1443 LOC a ~380 LOC - Mantener re-exports para compatibilidad con código existente
113 lines
3.9 KiB
Zig
113 lines
3.9 KiB
Zig
//! AdvancedTable - Ordenación y Búsqueda
|
|
//!
|
|
//! Funciones de ordenación y búsqueda extraídas del archivo principal.
|
|
|
|
const std = @import("std");
|
|
|
|
const types = @import("types.zig");
|
|
const state = @import("state.zig");
|
|
|
|
pub const Row = types.Row;
|
|
pub const SortDirection = types.SortDirection;
|
|
pub const AdvancedTableState = state.AdvancedTableState;
|
|
|
|
// =============================================================================
|
|
// Sorting
|
|
// =============================================================================
|
|
|
|
/// Sort rows by column value
|
|
pub fn sortRows(
|
|
table_state: *AdvancedTableState,
|
|
column_name: []const u8,
|
|
direction: SortDirection,
|
|
) void {
|
|
if (direction == .none) return;
|
|
if (table_state.rows.items.len < 2) return;
|
|
|
|
const len = table_state.rows.items.len;
|
|
var swapped = true;
|
|
|
|
while (swapped) {
|
|
swapped = false;
|
|
for (0..len - 1) |i| {
|
|
const val_a = table_state.rows.items[i].get(column_name);
|
|
const val_b = table_state.rows.items[i + 1].get(column_name);
|
|
const cmp = val_a.compare(val_b);
|
|
|
|
const should_swap = switch (direction) {
|
|
.ascending => cmp > 0,
|
|
.descending => cmp < 0,
|
|
.none => false,
|
|
};
|
|
|
|
if (should_swap) {
|
|
std.mem.swap(Row, &table_state.rows.items[i], &table_state.rows.items[i + 1]);
|
|
swapRowStates(table_state, i, i + 1);
|
|
swapped = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Swap state map entries between two row indices
|
|
pub fn swapRowStates(table_state: *AdvancedTableState, idx_a: usize, idx_b: usize) void {
|
|
const swapInMap = struct {
|
|
fn swap(map: anytype, a: usize, b: usize) void {
|
|
const val_a = map.get(a);
|
|
const val_b = map.get(b);
|
|
|
|
if (val_a != null and val_b != null) {
|
|
// Both exist - no change needed
|
|
} else if (val_a) |v| {
|
|
_ = map.remove(a);
|
|
map.put(b, v) catch {};
|
|
} else if (val_b) |v| {
|
|
_ = map.remove(b);
|
|
map.put(a, v) catch {};
|
|
}
|
|
}
|
|
}.swap;
|
|
|
|
swapInMap(&table_state.dirty_rows, idx_a, idx_b);
|
|
swapInMap(&table_state.new_rows, idx_a, idx_b);
|
|
swapInMap(&table_state.deleted_rows, idx_a, idx_b);
|
|
swapInMap(&table_state.validation_errors, idx_a, idx_b);
|
|
}
|
|
|
|
// =============================================================================
|
|
// Search Helpers
|
|
// =============================================================================
|
|
|
|
/// Case-insensitive prefix match for incremental search
|
|
pub fn startsWithIgnoreCase(haystack: []const u8, needle: []const u8) bool {
|
|
if (needle.len > haystack.len) return false;
|
|
if (needle.len == 0) return true;
|
|
|
|
for (needle, 0..) |needle_char, i| {
|
|
const haystack_char = haystack[i];
|
|
const needle_lower = if (needle_char >= 'A' and needle_char <= 'Z')
|
|
needle_char + 32
|
|
else
|
|
needle_char;
|
|
const haystack_lower = if (haystack_char >= 'A' and haystack_char <= 'Z')
|
|
haystack_char + 32
|
|
else
|
|
haystack_char;
|
|
|
|
if (needle_lower != haystack_lower) return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// =============================================================================
|
|
// Tests
|
|
// =============================================================================
|
|
|
|
test "startsWithIgnoreCase" {
|
|
try std.testing.expect(startsWithIgnoreCase("Hello World", "Hello"));
|
|
try std.testing.expect(startsWithIgnoreCase("Hello World", "hello"));
|
|
try std.testing.expect(startsWithIgnoreCase("hello world", "HELLO"));
|
|
try std.testing.expect(startsWithIgnoreCase("anything", ""));
|
|
try std.testing.expect(!startsWithIgnoreCase("Hello", "World"));
|
|
try std.testing.expect(!startsWithIgnoreCase("Hi", "Hello World"));
|
|
}
|