use ratatui::{
layout::{Alignment, Constraint, Direction, Layout, Margin, Rect},
style::{Color, Style},
widgets::{Block, Borders, Clear, Paragraph},
};
use crate::actions::AppAction;
const MODAL_WIDTH: u16 = 36;
const MODAL_HEIGHT: u16 = 7;
const YES_LABEL: &str = "[y] Yes";
const NO_LABEL: &str = "[n] No";
const BUTTON_GAP_WIDTH: u16 = 4;
fn centered_modal(area: Rect) -> Rect {
let vertical = Layout::default()
.direction(Direction::Vertical)
.constraints([
Constraint::Fill(1),
Constraint::Length(MODAL_HEIGHT),
Constraint::Fill(1),
])
.split(area);
let horizontal = Layout::default()
.direction(Direction::Horizontal)
.constraints([
Constraint::Fill(1),
Constraint::Length(MODAL_WIDTH),
Constraint::Fill(1),
])
.split(vertical[1]);
horizontal[1]
}
pub fn render_delete_confirm(frame: &mut ratatui::Frame, todo_title: Option<&str>) {
let modal = centered_modal(frame.area());
frame.render_widget(Clear, modal);
let block = Block::default()
.title(" Confirm Delete ")
.title_alignment(Alignment::Left)
.borders(Borders::ALL)
.border_style(Style::new().fg(Color::White));
frame.render_widget(block, modal);
let title = todo_title.unwrap_or("(unknown)");
let inner = modal.inner(Margin {
vertical: 1,
horizontal: 1,
});
let rows = Layout::default()
.direction(Direction::Vertical)
.constraints([
Constraint::Length(1),
Constraint::Length(1),
Constraint::Fill(1),
Constraint::Length(1),
])
.split(inner);
frame.render_widget(
Paragraph::new("Delete this todo?").alignment(Alignment::Center),
rows[0],
);
frame.render_widget(Paragraph::new(title).alignment(Alignment::Center), rows[1]);
if let Some((yes_rect, no_rect)) = button_rects(modal) {
frame.render_widget(Paragraph::new(YES_LABEL), yes_rect);
frame.render_widget(Paragraph::new(NO_LABEL), no_rect);
}
}
pub fn modal_rect(area: Rect) -> Rect {
centered_modal(area)
}
pub fn click_to_action(
row: u16,
col: u16,
area: Rect,
selected_id: Option<&str>,
) -> Option<AppAction> {
let modal = modal_rect(area);
let (yes_rect, no_rect) = button_rects(modal)?;
if row != yes_rect.y {
return None;
}
if col >= yes_rect.x && col < yes_rect.x.saturating_add(yes_rect.width) {
selected_id.map(|id| AppAction::DeleteTodo(id.to_string()))
} else if col >= no_rect.x && col < no_rect.x.saturating_add(no_rect.width) {
Some(AppAction::CloseDeleteConfirm)
} else {
None
}
}
fn button_rects(modal: Rect) -> Option<(Rect, Rect)> {
if modal.width < 3 || modal.height < 3 {
return None;
}
let inner = modal.inner(Margin {
vertical: 1,
horizontal: 1,
});
let yes_width = YES_LABEL.chars().count() as u16;
let no_width = NO_LABEL.chars().count() as u16;
let buttons_width = yes_width + BUTTON_GAP_WIDTH + no_width;
if inner.width < buttons_width || inner.height == 0 {
return None;
}
let button_row = inner.y + inner.height.saturating_sub(1);
let buttons_start = inner.x + (inner.width - buttons_width) / 2;
let yes_rect = Rect::new(buttons_start, button_row, yes_width, 1);
let no_rect = Rect::new(
buttons_start + yes_width + BUTTON_GAP_WIDTH,
button_row,
no_width,
1,
);
Some((yes_rect, no_rect))
}