lk-inside 0.3.1

A terminal user interface (TUI) application for interactive data analysis.
Documentation
//! A reusable UI component for displaying statistical summaries of dataframes.

use ratatui::{
    prelude::*,
    widgets::{Block, Borders, Row, Table, Cell, Paragraph}, // Added Paragraph
};
use polars::prelude::DataFrame;
use crate::ui::theme; // ADD THIS LINE

/// A widget for displaying a statistical summary (`DataFrame`) in a table format.
pub struct StatsPanel<'a> {
    /// The DataFrame containing the statistics to display.
    stats_df: &'a DataFrame,
}

impl<'a> StatsPanel<'a> {
        /// Creates a new `StatsPanel` instance.
    ///
    /// # Arguments
    ///
    /// * `stats_df` - A reference to the DataFrame containing the statistics.
    pub fn new(stats_df: &'a DataFrame) -> Self {
        Self { stats_df }
    }

        /// Renders the statistics table to the specified area of the terminal frame.
    ///
    /// The table displays the header and all rows of the `stats_df`.
    ///
    /// # Arguments
    ///
    /// * `self` - The `StatsPanel` instance.
    /// * `f` - A mutable reference to the `Frame` to draw on.
    /// * `area` - The `Rect` representing the area to draw the widget in.
    pub fn render(&self, f: &mut Frame, area: Rect) {
        let block = Block::default()
            .title("Descriptive Statistics")
            .borders(Borders::ALL)
            .border_style(theme::BORDER_STYLE); // REPLACED
        f.render_widget(block, area);

        // Inner chunks for title and table
        let inner_chunks = Layout::default()
            .direction(Direction::Vertical)
            .constraints([Constraint::Length(1), Constraint::Min(0)].as_ref())
            .margin(1) // Add some margin inside the block
            .split(area);

        // The title for the table
        let title = Paragraph::new("Summary").alignment(Alignment::Center);
        f.render_widget(title, inner_chunks[0]);

        // Prepare table headers and rows
        let headers = self.stats_df.get_column_names();
        let header_cells = headers.iter()
            .map(|s| Cell::from(s.to_string()).style(theme::ACCENT_BOLD_STYLE)) // REPLACED
            .collect::<Vec<Cell>>();

        let mut rows: Vec<Row> = Vec::new();
        for i in 0..self.stats_df.height() {
            if let Ok(row) = self.stats_df.get_row(i) {
                let cells = row.0.iter().map(|item| Cell::from(item.to_string())).collect::<Vec<Cell>>();
                rows.push(Row::new(cells));
            }
        }

        // Dynamically calculate widths based on header and content lengths
        let mut widths: Vec<u16> = headers.iter()
            .map(|name| name.len() as u16)
            .collect();

        for i in 0..self.stats_df.height() {
            if let Ok(row) = self.stats_df.get_row(i) {
                for (j, cell_data) in row.0.iter().enumerate() {
                    let cell_width = cell_data.to_string().len() as u16;
                    if j < widths.len() && cell_width > widths[j] {
                        widths[j] = cell_width;
                    }
                }
            }
        }
        
        let constraints: Vec<Constraint> = widths.iter()
            .map(|&w| Constraint::Length(w + 2)) // Add padding to calculated width
            .collect();

        let table = Table::new(rows, &constraints)
            .header(Row::new(header_cells))
            .block(Block::default())
            .row_highlight_style(Style::new().add_modifier(Modifier::BOLD).bg(theme::TEXT_COLOR)); // REPLACED
        
        f.render_widget(table, inner_chunks[1]);
    }
}