use ratatui::{
style::Style,
symbols::border,
text::Line,
widgets::{Block, Padding, Widget},
};
use ratatui_kit_macros::{Props, with_layout_style};
use crate::{AnyElement, Component, ComponentTheme, Palette, components::theme::resolve_style};
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct BorderTheme {
pub border_style: Style,
pub style: Style,
}
impl ComponentTheme for BorderTheme {
fn from_palette(palette: &Palette) -> Self {
Self {
border_style: Style::new().fg(palette.border),
style: Style::new(),
}
}
}
impl Default for BorderTheme {
fn default() -> Self {
Self::from_palette(&Palette::default())
}
}
#[with_layout_style]
#[derive(Props)]
pub struct BorderProps<'a> {
pub padding: Padding,
pub border_style: Option<Style>,
pub borders: ratatui::widgets::Borders,
pub border_set: border::Set<'static>,
pub style: Option<Style>,
pub children: Vec<AnyElement<'a>>,
pub top_title: Option<Line<'static>>,
pub bottom_title: Option<Line<'static>>,
}
impl Default for BorderProps<'_> {
fn default() -> Self {
Self {
padding: Padding::default(),
border_style: None,
borders: ratatui::widgets::Borders::ALL,
children: Vec::new(),
border_set: border::Set::default(),
style: None,
top_title: None,
bottom_title: None,
margin: Default::default(),
offset: Default::default(),
width: Default::default(),
height: Default::default(),
gap: Default::default(),
flex_direction: Default::default(),
justify_content: Default::default(),
}
}
}
pub struct Border {
pub padding: Padding,
pub border_style: Style,
pub borders: ratatui::widgets::Borders,
pub border_set: border::Set<'static>,
pub style: Style,
pub top_title: Option<Line<'static>>,
pub bottom_title: Option<Line<'static>>,
}
impl Border {
fn from_props(props: &BorderProps<'_>) -> Self {
Self {
padding: props.padding,
border_style: Style::default(),
borders: props.borders,
border_set: props.border_set,
style: Style::default(),
top_title: props.top_title.clone(),
bottom_title: props.bottom_title.clone(),
}
}
}
impl Component for Border {
type Props<'a> = BorderProps<'a>;
fn new(props: &Self::Props<'_>) -> Self {
Self::from_props(props)
}
fn update(
&mut self,
props: &mut Self::Props<'_>,
_hooks: crate::Hooks,
updater: &mut crate::ComponentUpdater,
) {
*self = Self::from_props(props);
let theme = updater.use_component_theme::<BorderTheme>();
self.border_style = resolve_style(theme.border_style, props.border_style);
self.style = resolve_style(theme.style, props.style);
updater.set_layout_style(props.layout_style());
updater.update_children(&mut props.children, None);
}
fn draw(&mut self, drawer: &mut crate::ComponentDrawer<'_, '_>) {
let mut block = Block::new()
.style(self.style)
.borders(self.borders)
.border_set(self.border_set)
.border_style(self.border_style)
.padding(self.padding);
if let Some(top_title) = &self.top_title {
block = block.title_top(top_title.clone());
}
if let Some(bottom_title) = &self.bottom_title {
block = block.title_bottom(bottom_title.clone());
}
let inner_area = block.inner(drawer.area);
block.render(drawer.area, drawer.buffer_mut());
drawer.area = inner_area;
}
}