ratatui_kit/components/
border.rs

1use ratatui::{
2    layout::{Constraint, Offset},
3    symbols::border,
4    text::Line,
5    widgets::{Block, Padding, Widget},
6};
7use ratatui_kit_macros::Props;
8
9use crate::{AnyElement, Component, layout_style::LayoutStyle};
10
11#[derive(Props)]
12pub struct BorderProps<'a> {
13    pub offset: Offset,
14    pub width: Constraint,
15    pub height: Constraint,
16    pub padding: Padding,
17    pub border_style: ratatui::style::Style,
18    pub borders: ratatui::widgets::Borders,
19    pub border_set: border::Set,
20    pub style: ratatui::style::Style,
21    pub children: Vec<AnyElement<'a>>,
22    pub top_title: Option<Line<'static>>,
23    pub bottom_title: Option<Line<'static>>,
24}
25
26impl Default for BorderProps<'_> {
27    fn default() -> Self {
28        Self {
29            offset: Offset::default(),
30            width: Constraint::default(),
31            height: Constraint::default(),
32            padding: Padding::default(),
33            border_style: ratatui::style::Style::default(),
34            borders: ratatui::widgets::Borders::ALL,
35            children: Vec::new(),
36            border_set: border::Set::default(),
37            style: ratatui::style::Style::default(),
38            top_title: None,
39            bottom_title: None,
40        }
41    }
42}
43
44impl From<&BorderProps<'_>> for LayoutStyle {
45    fn from(props: &BorderProps) -> Self {
46        LayoutStyle {
47            offset: props.offset,
48            width: props.width,
49            height: props.height,
50            ..Default::default()
51        }
52    }
53}
54
55pub struct Border {
56    pub padding: Padding,
57    pub border_style: ratatui::style::Style,
58    pub borders: ratatui::widgets::Borders,
59    pub border_set: border::Set,
60    pub style: ratatui::style::Style,
61    pub top_title: Option<Line<'static>>,
62    pub bottom_title: Option<Line<'static>>,
63}
64
65impl Component for Border {
66    type Props<'a> = BorderProps<'a>;
67
68    fn new(props: &Self::Props<'_>) -> Self {
69        Self {
70            padding: props.padding,
71            border_style: props.border_style,
72            borders: props.borders,
73            border_set: props.border_set,
74            style: props.style,
75            top_title: props.top_title.clone(),
76            bottom_title: props.bottom_title.clone(),
77        }
78    }
79
80    fn update(
81        &mut self,
82        props: &mut Self::Props<'_>,
83        _hooks: crate::Hooks,
84        updater: &mut crate::ComponentUpdater,
85    ) {
86        let layout_style = LayoutStyle::from(&*props);
87        *self = Self {
88            padding: props.padding,
89            border_style: props.border_style,
90            borders: props.borders,
91            border_set: props.border_set,
92            style: props.style,
93            top_title: props.top_title.clone(),
94            bottom_title: props.bottom_title.clone(),
95        };
96        updater.set_layout_style(layout_style);
97        updater.update_children(&mut props.children, None);
98    }
99
100    fn draw(&mut self, drawer: &mut crate::ComponentDrawer<'_, '_>) {
101        let mut block = Block::new()
102            .style(self.style)
103            .borders(self.borders)
104            .border_set(self.border_set)
105            .border_style(self.border_style)
106            .padding(self.padding);
107
108        if let Some(top_title) = &self.top_title {
109            block = block.title_top(top_title.clone());
110        }
111
112        if let Some(bottom_title) = &self.bottom_title {
113            block = block.title_bottom(bottom_title.clone());
114        }
115
116        let inner_area = block.inner(drawer.area);
117        block.render(drawer.area, drawer.buffer_mut());
118        drawer.area = inner_area;
119    }
120}