ratatui_kit/components/
alert_modal.rs1use crossterm::event::{Event, KeyCode, KeyEventKind};
4use ratatui::{
5 layout::{Alignment, Constraint},
6 style::{Color, Style},
7 text::Line,
8 widgets::Padding,
9};
10use ratatui_kit_macros::{Props, component, element};
11
12use crate::{
13 AnyElement, Handler, Hooks, UseEventHandler, UseInputLayer,
14 components::{Border, Modal, Text, TextParagraph},
15 input::{EventPriority, EventResult, EventScope},
16};
17
18#[derive(Props)]
19pub struct AlertModalProps {
20 pub open: bool,
21 pub title: Line<'static>,
22 pub message: TextParagraph<'static>,
23 pub close_hint: Option<Line<'static>>,
24 pub close_keys: Vec<KeyCode>,
25 pub on_close: Handler<'static, ()>,
26 pub width: Constraint,
27 pub height: Constraint,
28 pub style: Style,
29 pub border_style: Style,
30 pub title_style: Style,
31 pub message_style: Style,
32 pub padding: Padding,
33}
34
35impl Default for AlertModalProps {
36 fn default() -> Self {
37 Self {
38 open: false,
39 title: Line::from("Alert"),
40 message: TextParagraph::from(""),
41 close_hint: Some(Line::from("Esc / Enter").centered()),
42 close_keys: vec![KeyCode::Esc, KeyCode::Enter],
43 on_close: Handler::default(),
44 width: Constraint::Percentage(50),
45 height: Constraint::Length(6),
46 style: Style::default().dim(),
47 border_style: Style::default().fg(Color::Yellow),
48 title_style: Style::default().fg(Color::Yellow),
49 message_style: Style::default(),
50 padding: Padding::uniform(1),
51 }
52 }
53}
54
55#[component]
56pub fn AlertModal(props: &mut AlertModalProps, mut hooks: Hooks) -> impl Into<AnyElement<'static>> {
57 let layer = hooks.use_input_layer(props.open, true);
58 let close_keys = props.close_keys.clone();
59 let mut on_close = props.on_close.take();
60
61 hooks.use_event_handler(
62 EventScope::Layer(layer),
63 EventPriority::High,
64 move |event| {
65 if let Event::Key(key) = event
66 && key.kind == KeyEventKind::Press
67 && close_keys.contains(&key.code)
68 {
69 on_close(());
70 }
71 EventResult::Consumed
72 },
73 );
74
75 element!(Modal(
76 open: props.open,
77 layer: Some(layer),
78 width: props.width,
79 height: props.height,
80 style: props.style,
81 ) {
82 Border(
83 border_style: props.border_style,
84 top_title: props.title.clone().style(props.title_style).centered(),
85 bottom_title: props.close_hint.clone(),
86 padding: props.padding,
87 ) {
88 Text(
89 text: props.message.clone(),
90 style: props.message_style,
91 alignment: Alignment::Center,
92 wrap: true,
93 )
94 }
95 })
96}