ratatui_kit/components/
border.rs1use ratatui::{
19 style::Style,
20 symbols::border,
21 text::Line,
22 widgets::{Block, Padding, Widget},
23};
24use ratatui_kit_macros::{Props, with_layout_style};
25
26use crate::{AnyElement, Component, ComponentTheme, Palette, components::theme::resolve_style};
27
28#[non_exhaustive]
30#[derive(Debug, Clone, Copy, PartialEq, Eq)]
31pub struct BorderTheme {
32 pub border_style: Style,
34 pub style: Style,
36}
37
38impl ComponentTheme for BorderTheme {
39 fn from_palette(palette: &Palette) -> Self {
40 Self {
41 border_style: Style::new().fg(palette.border),
42 style: Style::new(),
43 }
44 }
45}
46
47impl Default for BorderTheme {
48 fn default() -> Self {
49 Self::from_palette(&Palette::default())
50 }
51}
52
53#[with_layout_style]
54#[derive(Props)]
55pub struct BorderProps<'a> {
57 pub padding: Padding,
59 pub border_style: Option<Style>,
61 pub borders: ratatui::widgets::Borders,
63 pub border_set: border::Set<'static>,
65 pub style: Option<Style>,
67 pub children: Vec<AnyElement<'a>>,
69 pub top_title: Option<Line<'static>>,
71 pub bottom_title: Option<Line<'static>>,
73}
74
75impl Default for BorderProps<'_> {
76 fn default() -> Self {
77 Self {
78 padding: Padding::default(),
79 border_style: None,
80 borders: ratatui::widgets::Borders::ALL,
81 children: Vec::new(),
82 border_set: border::Set::default(),
83 style: None,
84 top_title: None,
85 bottom_title: None,
86 margin: Default::default(),
87 offset: Default::default(),
88 width: Default::default(),
89 height: Default::default(),
90 gap: Default::default(),
91 flex_direction: Default::default(),
92 justify_content: Default::default(),
93 }
94 }
95}
96
97pub struct Border {
99 pub padding: Padding,
100 pub border_style: Style,
101 pub borders: ratatui::widgets::Borders,
102 pub border_set: border::Set<'static>,
103 pub style: Style,
104 pub top_title: Option<Line<'static>>,
105 pub bottom_title: Option<Line<'static>>,
106}
107
108impl Border {
109 fn from_props(props: &BorderProps<'_>) -> Self {
111 Self {
112 padding: props.padding,
113 border_style: Style::default(),
114 borders: props.borders,
115 border_set: props.border_set,
116 style: Style::default(),
117 top_title: props.top_title.clone(),
118 bottom_title: props.bottom_title.clone(),
119 }
120 }
121}
122
123impl Component for Border {
124 type Props<'a> = BorderProps<'a>;
125
126 fn new(props: &Self::Props<'_>) -> Self {
128 Self::from_props(props)
129 }
130
131 fn update(
133 &mut self,
134 props: &mut Self::Props<'_>,
135 _hooks: crate::Hooks,
136 updater: &mut crate::ComponentUpdater,
137 ) {
138 *self = Self::from_props(props);
139 let theme = updater.use_component_theme::<BorderTheme>();
142 self.border_style = resolve_style(theme.border_style, props.border_style);
143 self.style = resolve_style(theme.style, props.style);
144 updater.set_layout_style(props.layout_style());
146 updater.update_children(&mut props.children, None);
147 }
148
149 fn draw(&mut self, drawer: &mut crate::ComponentDrawer<'_, '_>) {
151 let mut block = Block::new()
153 .style(self.style)
154 .borders(self.borders)
155 .border_set(self.border_set)
156 .border_style(self.border_style)
157 .padding(self.padding);
158
159 if let Some(top_title) = &self.top_title {
161 block = block.title_top(top_title.clone());
162 }
163
164 if let Some(bottom_title) = &self.bottom_title {
166 block = block.title_bottom(bottom_title.clone());
167 }
168
169 let inner_area = block.inner(drawer.area);
171 block.render(drawer.area, drawer.buffer_mut());
173 drawer.area = inner_area;
175 }
176}