feat(advanced_table): BasicColors helper para colores configurables

- BasicColors struct: background, foreground, accent, header_bg opcionales
- TableColors.fromBasic(): deriva 26 colores desde 3-4 básicos
- BasicColors.toColor(): convierte cualquier tipo con r,g,b a Style.Color
- Detecta automáticamente tema claro/oscuro para derivar colores
- Presets dark()/light() y withAccent() para personalización

Permite que las aplicaciones pasen colores configurables desde sus
sistemas de config propios (como zsimifactu con resolveColor()).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
reugenio 2025-12-18 11:33:34 +01:00
parent bd95013ffc
commit 48ab2b749b
2 changed files with 127 additions and 0 deletions

View file

@ -28,6 +28,7 @@ pub const SortDirection = types.SortDirection;
pub const CRUDAction = types.CRUDAction; pub const CRUDAction = types.CRUDAction;
pub const ValidationResult = types.ValidationResult; pub const ValidationResult = types.ValidationResult;
pub const TableColors = types.TableColors; pub const TableColors = types.TableColors;
pub const BasicColors = types.BasicColors;
pub const TableConfig = types.TableConfig; pub const TableConfig = types.TableConfig;
pub const Row = types.Row; pub const Row = types.Row;

View file

@ -205,6 +205,41 @@ pub const CRUDAction = enum {
// Colors // Colors
// ============================================================================= // =============================================================================
/// Colores básicos para crear un esquema de tabla
/// El consumidor solo necesita especificar 3 colores y el resto se deriva
pub const BasicColors = struct {
/// Color de fondo de las filas
background: Style.Color,
/// Color del texto
foreground: Style.Color,
/// Color de acento (selección, focus, header activo)
accent: Style.Color = Style.Color.rgb(66, 135, 245),
// Opcionales - si null, se derivan de los básicos
header_bg: ?Style.Color = null,
row_alternate: ?Style.Color = null,
border: ?Style.Color = null,
/// Convierte cualquier tipo con campos r,g,b (y opcionalmente a) a Style.Color.
/// Útil para convertir desde otros tipos Color (ej: zcatconfig.Color).
///
/// Ejemplo:
/// ```zig
/// const cfg_color = config.panel_who_list_fondo; // zcatconfig.Color
/// const style_color = BasicColors.toColor(cfg_color); // Style.Color
/// ```
pub fn toColor(c: anytype) Style.Color {
const T = @TypeOf(c);
const has_alpha = @hasField(T, "a");
return .{
.r = c.r,
.g = c.g,
.b = c.b,
.a = if (has_alpha) c.a else 255,
};
}
};
/// Color scheme for AdvancedTable /// Color scheme for AdvancedTable
pub const TableColors = struct { pub const TableColors = struct {
// Header // Header
@ -246,6 +281,97 @@ pub const TableColors = struct {
indicator_modified: Style.Color = Style.Color.rgb(255, 180, 0), // Orange indicator_modified: Style.Color = Style.Color.rgb(255, 180, 0), // Orange
indicator_new: Style.Color = Style.Color.rgb(76, 175, 80), // Green indicator_new: Style.Color = Style.Color.rgb(76, 175, 80), // Green
indicator_deleted: Style.Color = Style.Color.rgb(244, 67, 54), // Red indicator_deleted: Style.Color = Style.Color.rgb(244, 67, 54), // Red
// =========================================================================
// Constructores
// =========================================================================
/// Crea un esquema de colores completo desde 3 colores básicos.
/// Deriva automáticamente hover, alternate, selected, etc.
///
/// Ejemplo de uso:
/// ```zig
/// const colors = TableColors.fromBasic(.{
/// .background = Style.Color.rgb(30, 30, 35),
/// .foreground = Style.Color.rgb(220, 220, 220),
/// .accent = Style.Color.rgb(40, 80, 120),
/// });
/// ```
pub fn fromBasic(opts: BasicColors) TableColors {
const bg = opts.background;
const fg = opts.foreground;
const accent = opts.accent;
// Detectar si es tema oscuro (fondo oscuro) o claro
const is_dark = (bg.r + bg.g + bg.b) < 384; // < 128 promedio
return .{
// Header - ligeramente diferente del fondo
.header_bg = opts.header_bg orelse (if (is_dark) bg.darken(10) else bg.darken(5)),
.header_fg = fg,
.header_hover = if (is_dark) bg.lighten(15) else bg.darken(10),
.header_sorted = if (is_dark) bg.lighten(10) else bg.darken(8),
.sort_indicator = accent,
// Rows
.row_normal = bg,
.row_alternate = opts.row_alternate orelse (if (is_dark) bg.lighten(5) else bg.darken(3)),
.row_hover = if (is_dark) bg.lighten(15) else bg.darken(8),
// Selection - basada en acento
.selected_cell = accent,
.selected_row = accent.darken(30),
// States - colores estándar universales
.state_modified = Style.Color.rgb(255, 255, 100),
.state_new = Style.Color.rgb(100, 200, 100),
.state_deleted = Style.Color.rgb(200, 100, 100),
.state_error = Style.Color.rgb(255, 80, 80),
// Text
.text_normal = fg,
.text_selected = Style.Color.rgb(255, 255, 255),
.text_disabled = if (is_dark) fg.darken(50) else fg.lighten(50),
// Cell editing
.cell_editing_bg = if (is_dark) bg.lighten(20) else bg.darken(10),
.cell_editing_border = accent,
// Borders
.border = opts.border orelse (if (is_dark) bg.lighten(20) else bg.darken(15)),
.focus_ring = accent,
// State indicators
.indicator_modified = Style.Color.rgb(255, 180, 0),
.indicator_new = Style.Color.rgb(76, 175, 80),
.indicator_deleted = Style.Color.rgb(244, 67, 54),
};
}
/// Preset de tema oscuro (valores por defecto)
pub fn dark() TableColors {
return .{};
}
/// Preset de tema claro
pub fn light() TableColors {
return fromBasic(.{
.background = Style.Color.rgb(250, 250, 250),
.foreground = Style.Color.rgb(30, 30, 30),
.accent = Style.Color.rgb(66, 135, 245),
});
}
/// Crea una copia con un nuevo color de acento
pub fn withAccent(self: TableColors, accent: Style.Color) TableColors {
var result = self;
result.selected_cell = accent;
result.selected_row = accent.darken(30);
result.sort_indicator = accent;
result.cell_editing_border = accent;
result.focus_ring = accent;
return result;
}
}; };
// ============================================================================= // =============================================================================