use ratatui::{
Frame,
layout::{Constraint, Flex, Layout, Rect},
style::{Modifier, Style},
text::{Line, Span},
widgets::{Block, Borders, Clear, Paragraph},
};
use travelagent_core::forge::ReactionContent;
use crate::app::App;
use crate::theme::Theme;
use crate::ui::styles;
pub const REACTIONS: [(ReactionContent, &str, &str); 8] = [
(ReactionContent::ThumbsUp, "\u{1F44D}", "+1"),
(ReactionContent::ThumbsDown, "\u{1F44E}", "-1"),
(ReactionContent::Laugh, "\u{1F604}", "laugh"),
(ReactionContent::Hooray, "\u{1F389}", "tada"),
(ReactionContent::Confused, "\u{1F615}", "?"),
(ReactionContent::Heart, "\u{2764}\u{FE0F}", "heart"),
(ReactionContent::Rocket, "\u{1F680}", "rocket"),
(ReactionContent::Eyes, "\u{1F440}", "eyes"),
];
pub fn render(frame: &mut Frame, area: Rect, app: &App, theme: &Theme) {
let desired_width: u16 = 72;
let popup_area = centered_rect(desired_width, 5, area);
frame.render_widget(Clear, popup_area);
let block = Block::default()
.title(" Reactions ")
.borders(Borders::ALL)
.style(styles::popup_style(theme))
.border_style(styles::border_style(theme, true));
let inner = block.inner(popup_area);
frame.render_widget(block, popup_area);
if inner.height == 0 {
return;
}
let rows = Layout::vertical([Constraint::Min(1), Constraint::Length(1)]).split(inner);
let mut spans: Vec<Span> = Vec::with_capacity(REACTIONS.len() * 3);
for (idx, (_, emoji, label)) in REACTIONS.iter().enumerate() {
if idx > 0 {
spans.push(Span::raw(" "));
}
let entry = format!("{emoji} {label}");
let style = if idx == app.reaction_picker_cursor {
Style::default().add_modifier(Modifier::REVERSED)
} else {
Style::default()
};
spans.push(Span::styled(entry, style));
}
let body = Paragraph::new(Line::from(spans)).alignment(ratatui::layout::Alignment::Center);
frame.render_widget(body, rows[0]);
if rows.len() > 1 && rows[1].height > 0 {
let hint = Paragraph::new(Line::from(Span::styled(
"\u{2190}/\u{2192} move Enter select 1-8 quick Esc cancel",
styles::dim_style(theme),
)))
.alignment(ratatui::layout::Alignment::Center);
frame.render_widget(hint, rows[1]);
}
}
#[must_use]
fn centered_rect(width: u16, height: u16, area: Rect) -> Rect {
let width = width.min(area.width);
let height = height.min(area.height);
let vertical = Layout::vertical([Constraint::Length(height)]).flex(Flex::Center);
let horizontal = Layout::horizontal([Constraint::Length(width)]).flex(Flex::Center);
let [area] = vertical.areas(area);
let [area] = horizontal.areas(area);
area
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn reactions_table_has_eight_distinct_contents() {
assert_eq!(REACTIONS.len(), 8);
for (i, a) in REACTIONS.iter().enumerate() {
for (j, b) in REACTIONS.iter().enumerate() {
assert_eq!(i == j, a.0 == b.0);
}
}
}
}