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