ratatui_kit/components/
modal.rs

1use 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}