use crate::app_state_container::SelectionMode;
use crate::ui::rendering::table_render_context::TableRenderContext;
use ratatui::{
layout::Constraint,
prelude::*,
style::{Color, Modifier, Style},
widgets::{Block, Borders, Cell, Paragraph, Row, Table},
};
pub fn render_table(f: &mut Frame, area: Rect, ctx: &TableRenderContext) {
if ctx.row_count == 0 {
let empty = Paragraph::new("No results found")
.block(Block::default().borders(Borders::ALL).title("Results"))
.style(Style::default().fg(Color::Yellow));
f.render_widget(empty, area);
return;
}
let header = build_header_row(ctx);
let rows = build_data_rows(ctx);
let widths = calculate_column_widths(ctx);
let table = Table::new(rows, widths)
.header(header)
.block(
Block::default()
.borders(Borders::ALL)
.title(format!("Results ({} rows)", ctx.row_count)),
)
.column_spacing(1)
.row_highlight_style(
Style::default()
.bg(Color::DarkGray)
.add_modifier(Modifier::BOLD),
);
f.render_widget(table, area);
}
fn build_header_row(ctx: &TableRenderContext) -> Row<'static> {
let mut header_cells: Vec<Cell> = Vec::new();
if ctx.show_row_numbers {
header_cells.push(
Cell::from("#").style(
Style::default()
.fg(Color::Magenta)
.add_modifier(Modifier::BOLD),
),
);
}
let mut last_was_pinned = false;
for (visual_pos, header) in ctx.column_headers.iter().enumerate() {
let is_pinned = ctx.is_pinned_column(visual_pos);
if last_was_pinned && !is_pinned && ctx.pinned_count > 0 {
header_cells.push(
Cell::from("│").style(
Style::default()
.fg(Color::DarkGray)
.add_modifier(Modifier::BOLD),
),
);
}
let sort_indicator = ctx.get_sort_indicator(visual_pos);
let is_crosshair = ctx.is_selected_column(visual_pos);
let column_indicator = if is_crosshair { " [*]" } else { "" };
let pin_indicator = if is_pinned { "📌 " } else { "" };
let mut style = if is_pinned {
Style::default()
.bg(Color::Rgb(40, 40, 80)) .fg(Color::White)
.add_modifier(Modifier::BOLD)
} else {
Style::default()
.fg(Color::Cyan)
.add_modifier(Modifier::BOLD)
};
if is_crosshair {
style = style.fg(Color::Yellow).add_modifier(Modifier::UNDERLINED);
}
header_cells.push(
Cell::from(format!(
"{pin_indicator}{header}{sort_indicator}{column_indicator}"
))
.style(style),
);
last_was_pinned = is_pinned;
}
Row::new(header_cells)
}
fn build_data_rows(ctx: &TableRenderContext) -> Vec<Row<'static>> {
ctx.data_rows
.iter()
.enumerate()
.map(|(row_idx, row_data)| {
let mut cells: Vec<Cell> = Vec::new();
if ctx.show_row_numbers {
let row_num = ctx.row_viewport.start + row_idx + 1;
cells.push(
Cell::from(row_num.to_string()).style(Style::default().fg(Color::DarkGray)),
);
}
let is_current_row = ctx.is_selected_row(row_idx);
let mut last_was_pinned = false;
for (col_idx, val) in row_data.iter().enumerate() {
let is_pinned = ctx.is_pinned_column(col_idx);
if last_was_pinned && !is_pinned && ctx.pinned_count > 0 {
cells.push(Cell::from("│").style(Style::default().fg(Color::DarkGray)));
}
let is_selected_column = ctx.is_selected_column(col_idx);
let mut cell = Cell::from(val.clone());
if !is_current_row && ctx.cell_matches_filter(val) {
cell = cell.style(Style::default().fg(Color::Magenta));
}
if is_pinned && !is_current_row {
cell = cell.style(Style::default().bg(Color::Rgb(20, 20, 40)));
}
cell = match ctx.selection_mode {
SelectionMode::Cell if is_current_row && is_selected_column => {
cell.style(
Style::default()
.bg(Color::Yellow)
.fg(Color::Black)
.add_modifier(Modifier::BOLD),
)
}
SelectionMode::Row if is_current_row => {
if is_selected_column {
cell.style(
Style::default()
.bg(Color::Yellow)
.fg(Color::Black)
.add_modifier(Modifier::BOLD),
)
} else if is_pinned {
cell.style(Style::default().bg(Color::Rgb(60, 80, 120)))
} else {
cell.style(Style::default().bg(Color::Rgb(70, 70, 70)))
}
}
_ if is_selected_column => {
if is_pinned {
cell.style(Style::default().bg(Color::Rgb(40, 60, 100)))
} else {
cell.style(Style::default().bg(Color::Rgb(50, 50, 50)))
}
}
_ if is_pinned => {
cell.style(Style::default().bg(Color::Rgb(20, 30, 50)))
}
_ => cell,
};
cells.push(cell);
last_was_pinned = is_pinned;
}
let row_style = if is_current_row {
Style::default()
.bg(Color::DarkGray)
.add_modifier(Modifier::BOLD)
} else {
Style::default()
};
Row::new(cells).style(row_style)
})
.collect()
}
fn calculate_column_widths(ctx: &TableRenderContext) -> Vec<Constraint> {
let mut widths: Vec<Constraint> = Vec::new();
if ctx.show_row_numbers {
widths.push(Constraint::Length(8)); }
let mut last_was_pinned = false;
for (idx, &width) in ctx.column_widths.iter().enumerate() {
let is_pinned = ctx.is_pinned_column(idx);
if last_was_pinned && !is_pinned && ctx.pinned_count > 0 {
widths.push(Constraint::Length(1)); }
widths.push(Constraint::Length(width));
last_was_pinned = is_pinned;
}
widths
}