ratatui_kit/components/
modal.rs

1use ratatui::{
2    layout::{Constraint, Flex, Layout, Margin, Offset},
3    style::Style,
4    widgets::{Block, Clear},
5};
6use ratatui_kit_macros::Props;
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#[derive(Default, Props)]
41pub struct ModalProps<'a> {
42    pub children: Vec<AnyElement<'a>>,
43    pub margin: Margin,
44    pub offset: Offset,
45    pub width: Constraint,
46    pub height: Constraint,
47    pub style: Style,
48    pub placement: Placement,
49    pub open: bool,
50}
51
52pub struct Modal {
53    pub open: bool,
54    pub margin: Margin,
55    pub offset: Offset,
56    pub width: Constraint,
57    pub height: Constraint,
58    pub placement: Placement,
59    pub style: Style,
60}
61
62impl Component for Modal {
63    type Props<'a> = ModalProps<'a>;
64    fn new(props: &Self::Props<'_>) -> Self {
65        Modal {
66            open: props.open,
67            margin: props.margin,
68            offset: props.offset,
69            width: props.width,
70            height: props.height,
71            style: props.style,
72            placement: props.placement,
73        }
74    }
75
76    fn update(
77        &mut self,
78        props: &mut Self::Props<'_>,
79        _hooks: crate::Hooks,
80        updater: &mut crate::ComponentUpdater,
81    ) {
82        self.open = props.open;
83        self.margin = props.margin;
84        self.offset = props.offset;
85        self.width = props.width;
86        self.height = props.height;
87        self.style = props.style;
88        self.placement = props.placement;
89
90        if self.open {
91            updater.update_children(props.children.iter_mut(), None);
92        }
93
94        updater.set_layout_style(LayoutStyle {
95            width: Constraint::Percentage(0),
96            height: Constraint::Percentage(0),
97            ..Default::default()
98        });
99    }
100
101    fn draw(&mut self, drawer: &mut crate::ComponentDrawer<'_, '_>) {
102        if self.open {
103            let area = drawer.frame.area();
104            let area = area.inner(self.margin).offset(self.offset);
105            let block = Block::default().style(self.style);
106            drawer.frame.render_widget(block, area);
107
108            let [v, h] = self.placement.to_flex();
109
110            let vertical = Layout::vertical([self.height]).flex(v).split(area)[0];
111            let horizontal = Layout::horizontal([self.width]).flex(h).split(vertical)[0];
112
113            drawer.frame.render_widget(Clear, horizontal);
114            drawer.area = horizontal;
115        }
116    }
117}