ratatui_kit/components/
modal.rs1use ratatui::{
2 layout::{Constraint, Flex, Layout, Margin, Offset},
3 style::Style,
4 widgets::{Block, Clear, Widget},
5};
6use ratatui_kit_macros::{Props, with_layout_style};
7
8use crate::{AnyElement, Component, layout_style::LayoutStyle};
9
10#[derive(Default, Clone, Copy)]
11pub enum Placement {
12 Top,
13 TopLeft,
14 TopRight,
15 Bottom,
16 BottomLeft,
17 BottomRight,
18 #[default]
19 Center,
20 Left,
21 Right,
22}
23
24impl Placement {
25 pub fn to_flex(&self) -> [Flex; 2] {
26 match self {
27 Placement::Top => [Flex::Start, Flex::Center],
28 Placement::TopLeft => [Flex::Start, Flex::Start],
29 Placement::TopRight => [Flex::Start, Flex::End],
30 Placement::Bottom => [Flex::End, Flex::Center],
31 Placement::BottomLeft => [Flex::End, Flex::Start],
32 Placement::BottomRight => [Flex::End, Flex::End],
33 Placement::Center => [Flex::Center, Flex::Center],
34 Placement::Left => [Flex::Center, Flex::Start],
35 Placement::Right => [Flex::Center, Flex::End],
36 }
37 }
38}
39
40#[with_layout_style(margin, offset, width, height)]
41#[derive(Default, Props)]
42pub struct ModalProps<'a> {
43 pub children: Vec<AnyElement<'a>>,
44 pub style: Style,
45 pub placement: Placement,
46 pub open: bool,
47}
48
49pub struct Modal {
50 pub open: bool,
51 pub margin: Margin,
52 pub offset: Offset,
53 pub width: Constraint,
54 pub height: Constraint,
55 pub placement: Placement,
56 pub style: Style,
57}
58
59impl Component for Modal {
60 type Props<'a> = ModalProps<'a>;
61 fn new(props: &Self::Props<'_>) -> Self {
62 Modal {
63 open: props.open,
64 margin: props.margin,
65 offset: props.offset,
66 width: props.width,
67 height: props.height,
68 style: props.style,
69 placement: props.placement,
70 }
71 }
72
73 fn update(
74 &mut self,
75 props: &mut Self::Props<'_>,
76 _hooks: crate::Hooks,
77 updater: &mut crate::ComponentUpdater,
78 ) {
79 self.open = props.open;
80 self.margin = props.margin;
81 self.offset = props.offset;
82 self.width = props.width;
83 self.height = props.height;
84 self.style = props.style;
85 self.placement = props.placement;
86
87 if self.open {
88 updater.update_children(props.children.iter_mut(), None);
89 }
90
91 updater.set_layout_style(LayoutStyle {
92 width: Constraint::Percentage(0),
93 height: Constraint::Percentage(0),
94 ..Default::default()
95 });
96 }
97
98 fn draw(&mut self, drawer: &mut crate::ComponentDrawer<'_, '_>) {
99 if self.open {
100 let area = drawer.buffer_mut().area();
101 let area = area.inner(self.margin).offset(self.offset);
102 let block = Block::default().style(self.style);
103 block.render(area, drawer.buffer_mut());
104
105 let [v, h] = self.placement.to_flex();
106
107 let vertical = Layout::vertical([self.height]).flex(v).split(area)[0];
108 let horizontal = Layout::horizontal([self.width]).flex(h).split(vertical)[0];
109
110 Clear.render(horizontal, drawer.buffer_mut());
111 drawer.area = horizontal;
112 }
113 }
114}