use crate::components::footer::Footer;
use crate::styles::theme;
use ratatui::prelude::*;
use ratatui::widgets::{Block, Borders, Clear, Paragraph};
#[derive(Debug)]
pub struct PopupRenderResult {
pub content_area: Rect,
}
pub struct Popup<'a> {
pub width_percent: u16,
pub height_percent: u16,
pub dim_background: bool,
pub title: Option<String>,
pub show_border: bool,
pub footer: Option<&'a str>,
}
impl<'a> Popup<'a> {
#[must_use]
pub fn new() -> Self {
Self {
width_percent: 70,
height_percent: 50,
dim_background: true,
title: None,
show_border: true,
footer: None,
}
}
#[must_use]
pub fn width(mut self, percent: u16) -> Self {
self.width_percent = percent;
self
}
#[must_use]
pub fn height(mut self, percent: u16) -> Self {
self.height_percent = percent;
self
}
#[must_use]
pub fn dim_background(mut self, dim: bool) -> Self {
self.dim_background = dim;
self
}
pub fn title<S: Into<String>>(mut self, title: S) -> Self {
self.title = Some(title.into());
self
}
#[must_use]
pub fn border(mut self, show: bool) -> Self {
self.show_border = show;
self
}
#[must_use]
pub fn footer(mut self, footer: &'a str) -> Self {
self.footer = Some(footer);
self
}
pub fn render(&self, frame: &mut Frame, area: Rect) -> PopupRenderResult {
let t = theme();
let popup_width = (f32::from(area.width) * (f32::from(self.width_percent) / 100.0)) as u16;
let popup_height =
(f32::from(area.height) * (f32::from(self.height_percent) / 100.0)) as u16;
let popup_x = area.x + (area.width.saturating_sub(popup_width)) / 2;
let popup_y = area.y + (area.height.saturating_sub(popup_height)) / 2;
let popup_area = Rect::new(popup_x, popup_y, popup_width, popup_height);
if self.dim_background {
let dim = Block::default().style(t.dim_style());
frame.render_widget(dim, area);
}
frame.render_widget(Clear, popup_area);
let inner_area = if self.show_border {
let block = Block::default()
.borders(Borders::ALL)
.border_type(t.border_focused_type)
.border_style(Style::default().fg(t.border_focused))
.style(t.background_style());
let inner = block.inner(popup_area);
frame.render_widget(block, popup_area);
inner
} else {
popup_area
};
let mut constraints = Vec::new();
if self.title.is_some() {
constraints.push(Constraint::Length(1));
}
constraints.push(Constraint::Min(0));
if self.footer.is_some() {
constraints.push(Constraint::Length(2));
}
let chunks = Layout::default()
.direction(Direction::Vertical)
.constraints(constraints)
.split(inner_area);
let mut chunk_idx = 0;
if let Some(ref title_text) = self.title {
let title_para = Paragraph::new(title_text.as_str())
.alignment(Alignment::Center)
.style(t.title_style());
frame.render_widget(title_para, chunks[chunk_idx]);
chunk_idx += 1;
}
let content_area = chunks[chunk_idx];
chunk_idx += 1;
if let Some(footer_text) = self.footer {
let _ = Footer::render(frame, chunks[chunk_idx], footer_text);
}
PopupRenderResult { content_area }
}
}
impl Default for Popup<'_> {
fn default() -> Self {
Self::new()
}
}