use ratatui::{
layout::Rect,
style::{Modifier, Style},
text::{Line, Span},
widgets::{Block, Borders, Clear, Paragraph},
Frame,
};
use unicode_width::UnicodeWidthStr;
use crate::app_state::RowFormState;
use crate::data::column::ColumnMeta;
use crate::theme::EverforestTheme as T;
use crate::ui::popup::centered_rect;
pub fn render_row_form_popup(
frame: &mut Frame,
state: &RowFormState,
columns: &[ColumnMeta],
area: Rect,
) {
let n = state.fields.len().min(columns.len());
let base = centered_rect(60, 70, area);
let wanted = (n as u16).saturating_add(3).clamp(4, area.height);
let h = wanted.min(base.height.max(4)).min(area.height);
let popup_area = Rect {
x: base.x,
y: base.y + base.height.saturating_sub(h) / 2,
width: base.width,
height: h,
};
frame.render_widget(Clear, popup_area);
let block = Block::default()
.title(" New row (↑↓/Tab field, ←→ cursor, Enter insert, Esc cancel) ")
.borders(Borders::ALL)
.border_style(Style::default().fg(T::PURPLE));
let inner_h = popup_area.height.saturating_sub(2) as usize;
let rows = inner_h.saturating_sub(1).max(1);
let start = if n <= rows {
0
} else {
state.focus.saturating_sub(rows / 2).min(n - rows)
};
let end = (start + rows).min(n);
let name_w = columns[..n]
.iter()
.map(|c| c.name.width())
.max()
.unwrap_or(4)
.clamp(4, 20);
let mut lines: Vec<Line> = Vec::with_capacity(rows + 1);
for (i, meta) in columns.iter().enumerate().take(end).skip(start) {
let focused = i == state.focus;
let bad = state.errors.get(i).and_then(|e| e.as_ref()).is_some();
let mut name = meta.name.clone();
if name.width() > name_w {
name.truncate(
name.char_indices()
.map(|(b, _)| b)
.nth(name_w - 1)
.unwrap_or(name.len()),
);
name.push('…');
}
let pad = name_w.saturating_sub(name.width());
let label_style = if bad {
Style::default().fg(T::RED)
} else if focused {
Style::default().fg(T::YELLOW)
} else {
Style::default().fg(T::FG)
};
let value_style = if bad {
Style::default().fg(T::RED)
} else {
Style::default().fg(T::FG)
};
let line_style = if focused {
label_style.bg(T::BG2).add_modifier(Modifier::BOLD)
} else {
label_style
};
lines.push(Line::from(vec![
Span::styled(if focused { "> " } else { " " }, line_style),
Span::styled(format!("{} ", meta.col_type.icon()), line_style),
Span::styled(format!("{}{} ", name, " ".repeat(pad)), line_style),
Span::styled("│ ", Style::default().fg(T::BG2)),
Span::styled(state.fields[i].as_str().to_string(), value_style),
]));
}
let empty = state
.fields
.iter()
.filter(|f| f.as_str().trim().is_empty())
.count();
let footer = if state.confirm_empty {
Line::from(Span::styled(
format!(
" {} of {} fields empty → NULL. Enter again to insert.",
empty, n
),
Style::default().fg(T::ORANGE).add_modifier(Modifier::BOLD),
))
} else {
match state.errors.get(state.focus).and_then(|e| e.as_ref()) {
Some(err) => Line::from(Span::styled(
format!(" {}", err),
Style::default().fg(T::RED),
)),
None => {
let hint = columns
.get(state.focus)
.map(|c| format!(" {} · {} — empty leaves NULL", c.name, c.col_type.name()))
.unwrap_or_default();
Line::from(Span::styled(hint, Style::default().fg(T::GREY1)))
}
}
};
lines.push(footer);
frame.render_widget(Paragraph::new(lines).block(block), popup_area);
let value_x = 2 + 2 + name_w as u16 + 1 + 2;
if (start..end).contains(&state.focus) {
let row = (state.focus - start) as u16;
frame.set_cursor_position((
popup_area.x + 1 + value_x + state.fields[state.focus].cursor_pos(),
popup_area.y + 1 + row,
));
}
}