tuitab 0.3.8

Terminal tabular data explorer — CSV/JSON/Parquet/Excel/SQLite viewer with filtering, sorting, pivot tables, and charts
use crate::theme::EverforestTheme as T;
use crate::ui::popup::centered_rect;
use ratatui::{
    layout::{Alignment, Rect},
    style::Style,
    text::{Line, Span},
    widgets::{Block, Borders, Clear, List, ListItem, Paragraph},
    Frame,
};

pub fn render_confirm_popup(frame: &mut Frame, message: &str, area: Rect) {
    let popup_area = centered_rect(40, 20, area);
    frame.render_widget(Clear, popup_area);

    let block = Block::default()
        .title(" Confirm ")
        .borders(Borders::ALL)
        .border_style(Style::default().fg(T::RED));

    let lines = vec![
        Line::from(""),
        Line::from(vec![Span::styled(message, Style::default().fg(T::YELLOW))]),
    ];

    let paragraph = Paragraph::new(lines)
        .alignment(Alignment::Center)
        .block(block);
    frame.render_widget(paragraph, popup_area);
}

pub fn render_chart_agg_popup(frame: &mut Frame, app: &crate::app::App, area: Rect) {
    let popup_area = centered_rect(35, 50, area);
    frame.render_widget(Clear, popup_area);

    let items: Vec<ListItem> = crate::types::ChartAgg::all()
        .iter()
        .enumerate()
        .map(|(i, agg)| {
            let is_active = i == app.chart.agg_index;
            let prefix = if is_active { "> " } else { "  " };
            let style = if is_active {
                Style::default().fg(T::YELLOW)
            } else {
                Style::default().fg(T::FG)
            };
            ListItem::new(Line::from(Span::styled(
                format!("{}{}", prefix, agg.label()),
                style,
            )))
        })
        .collect();

    let list = List::new(items).block(
        Block::default()
            .title(" Select aggregation (↑↓, Enter) ")
            .borders(Borders::ALL)
            .border_style(Style::default().fg(T::PURPLE)),
    );
    frame.render_widget(list, popup_area);
}

pub fn render_copy_format_popup(frame: &mut Frame, app: &crate::app::App, area: Rect) {
    use crate::types::CopyPending;
    let popup_area = centered_rect(44, 40, area);
    frame.render_widget(Clear, popup_area);

    let pending = match app.copy.pending {
        Some(p) => p,
        None => return,
    };

    let row_options: &[&str] = &[
        "TSV (with header)",
        "CSV (with header)",
        "JSON (array of objects)",
        "Markdown table",
    ];
    let col_options: &[&str] = &[
        "Newline-separated",
        "Comma-separated",
        "Comma-separated, single-quoted",
    ];

    let (title, options) = match pending {
        CopyPending::SmartRows => {
            let df = &app.stack.active().dataframe;
            let n_sel = df.selected_rows.len();
            let n_col_sel = df.columns.iter().filter(|c| c.selected).count();
            let col_note = if n_col_sel > 0 {
                format!(", {} cols", n_col_sel)
            } else {
                String::new()
            };
            let t = if n_sel > 0 {
                format!(" Copy {} Selected Rows{} ", n_sel, col_note)
            } else {
                format!(" Copy Current Row{} ", col_note)
            };
            (t, row_options)
        }
        CopyPending::SmartColumn => {
            let count = app.stack.active().dataframe.selected_rows.len();
            (
                format!(" Copy Column Values ({} selected rows) ", count),
                col_options,
            )
        }
        CopyPending::WholeColumn => {
            let col = app.stack.active().cursor_col;
            let name = &app.stack.active().dataframe.columns[col].name;
            (format!(" Copy Column \"{}\" ", name), col_options)
        }
        CopyPending::WholeTable => {
            let df = &app.stack.active().dataframe;
            let n_col_sel = df.columns.iter().filter(|c| c.selected).count();
            let col_note = if n_col_sel > 0 {
                format!(" ({} cols selected)", n_col_sel)
            } else {
                String::new()
            };
            (format!(" Copy Table{} ", col_note), row_options)
        }
    };

    let items: Vec<ListItem> = options
        .iter()
        .enumerate()
        .map(|(i, opt)| {
            let is_active = i == app.copy.format_index;
            let prefix = if is_active { "" } else { "  " };
            let style = if is_active {
                Style::default().fg(T::YELLOW).bg(T::BG2)
            } else {
                Style::default().fg(T::FG)
            };
            ListItem::new(Line::from(Span::styled(
                format!("{}{}", prefix, opt),
                style,
            )))
        })
        .collect();

    let list = List::new(items).block(
        Block::default()
            .title(title)
            .borders(Borders::ALL)
            .border_style(Style::default().fg(T::PURPLE))
            .title_bottom(Line::from(Span::styled(
                " ↑↓ navigate · Enter apply · Esc cancel ",
                Style::default().fg(T::GREY1),
            ))),
    );
    frame.render_widget(list, popup_area);
}