use ratatui::{
buffer::Buffer,
layout::Rect,
style::{Color, Modifier, Style},
text::{Line, Span},
widgets::{Block, Borders, Paragraph, Widget, Wrap},
};
pub struct ErrorBanner<'a> {
message: &'a str,
}
impl<'a> ErrorBanner<'a> {
pub const fn new(message: &'a str) -> Self {
Self { message }
}
pub fn required_height(&self, width: u16) -> u16 {
if self.message.is_empty() || width == 0 {
return 0;
}
let content_width = width.saturating_sub(4) as usize;
if content_width == 0 {
return 3; }
let line_count: usize = self
.message
.lines()
.map(|line| {
let char_count = line.chars().count();
if char_count == 0 {
1 } else {
char_count.div_ceil(content_width)
}
})
.sum();
(line_count as u16).max(1) + 2
}
}
impl Widget for ErrorBanner<'_> {
fn render(self, area: Rect, buf: &mut Buffer) {
if self.message.is_empty() || area.is_empty() {
return;
}
let block = Block::default()
.borders(Borders::ALL)
.border_style(Style::new().fg(Color::Red))
.title(Span::styled(
" Error ",
Style::new().fg(Color::Red).add_modifier(Modifier::BOLD),
))
.title_top(
Line::from(vec![
Span::styled("[y]", Style::new().fg(Color::DarkGray)),
Span::styled(" copy ", Style::new().fg(Color::Gray)),
])
.right_aligned(),
);
let error_style = Style::new().fg(Color::Red);
let lines: Vec<Line> = self
.message
.lines()
.map(|line| Line::from(Span::styled(line.to_string(), error_style)))
.collect();
Paragraph::new(lines)
.block(block)
.wrap(Wrap { trim: false })
.render(area, buf);
}
}