ratatui_interact/
lib.rs

1//! # TUI Extension
2//!
3//! Reusable UI components extending ratatui with focus management and mouse support.
4//!
5//! This crate provides interactive UI components that integrate with ratatui's
6//! widget system while adding:
7//!
8//! - **Focus Management**: Tab navigation between components
9//! - **Click Regions**: Mouse click support with hit-testing
10//! - **Composition**: Container-based component hierarchies for dialogs
11//!
12//! ## Quick Start
13//!
14//! ```rust,ignore
15//! use ratatui_interact::prelude::*;
16//!
17//! // Create component state
18//! let mut checkbox_state = CheckBoxState::new(false);
19//! let mut input_state = InputState::new("Hello");
20//! let button_state = ButtonState::enabled();
21//!
22//! // Use in your render function
23//! fn render(frame: &mut Frame, area: Rect) {
24//!     let checkbox = CheckBox::new("Enable", &checkbox_state);
25//!     let input = Input::new(&input_state).label("Name");
26//!     let button = Button::new("Submit", &button_state);
27//!
28//!     // Render and get click regions
29//!     let cb_region = checkbox.render_stateful(area, frame.buffer_mut());
30//!     let input_region = input.render_stateful(frame, input_area);
31//!     let btn_region = button.render_stateful(button_area, frame.buffer_mut());
32//! }
33//! ```
34//!
35//! ## Components
36//!
37//! ### CheckBox
38//!
39//! A toggleable checkbox with customizable symbols:
40//!
41//! ```rust
42//! use ratatui_interact::components::{CheckBox, CheckBoxState, CheckBoxStyle};
43//!
44//! let mut state = CheckBoxState::new(false);
45//! let checkbox = CheckBox::new("Dark mode", &state)
46//!     .style(CheckBoxStyle::unicode()); // ☑ / ☐
47//!
48//! // Toggle on user action
49//! state.toggle();
50//! ```
51//!
52//! ### Input
53//!
54//! A text input field with cursor and editing support:
55//!
56//! ```rust
57//! use ratatui_interact::components::{Input, InputState};
58//!
59//! let mut state = InputState::new("Initial text");
60//!
61//! // Edit text
62//! state.insert_char('!');
63//! state.move_left();
64//! state.delete_char_backward();
65//! ```
66//!
67//! ### Button
68//!
69//! Buttons with multiple display variants:
70//!
71//! ```rust
72//! use ratatui_interact::components::{Button, ButtonState, ButtonVariant, ButtonStyle};
73//!
74//! let state = ButtonState::enabled();
75//!
76//! // Different styles
77//! let simple = Button::new("OK", &state);
78//! let with_icon = Button::new("Save", &state).icon("💾");
79//! let block_style = Button::new("Submit", &state).variant(ButtonVariant::Block);
80//! let toggle = Button::new("Active", &ButtonState::toggled(true))
81//!     .variant(ButtonVariant::Toggle);
82//! ```
83//!
84//! ### PopupDialog
85//!
86//! A container for popup dialogs with focus management:
87//!
88//! ```rust,ignore
89//! use ratatui_interact::components::{DialogConfig, DialogState, PopupDialog};
90//! use ratatui_interact::traits::ContainerAction;
91//!
92//! let config = DialogConfig::new("Settings")
93//!     .width_percent(50)
94//!     .ok_cancel();
95//!
96//! let mut state = DialogState::new(MyContent::default());
97//! state.show();
98//!
99//! let mut dialog = PopupDialog::new(&config, &mut state, |frame, area, content| {
100//!     // Render dialog content
101//! });
102//! dialog.render(frame);
103//!
104//! // Handle events
105//! match dialog.handle_key(key_event) {
106//!     EventResult::Action(ContainerAction::Submit) => { /* save */ }
107//!     EventResult::Action(ContainerAction::Close) => { /* cancel */ }
108//!     _ => {}
109//! }
110//! ```
111//!
112//! ## Focus Management
113//!
114//! The `FocusManager` handles Tab navigation:
115//!
116//! ```rust
117//! use ratatui_interact::state::FocusManager;
118//!
119//! #[derive(Clone, PartialEq, Eq, Hash)]
120//! enum Element { Name, Email, Submit }
121//!
122//! let mut focus = FocusManager::new();
123//! focus.register(Element::Name);
124//! focus.register(Element::Email);
125//! focus.register(Element::Submit);
126//!
127//! // Navigate
128//! focus.next(); // Name -> Email
129//! focus.prev(); // Email -> Name
130//! focus.set(Element::Submit); // Jump to Submit
131//! ```
132//!
133//! ## Click Regions
134//!
135//! Track clickable areas with `ClickRegionRegistry`:
136//!
137//! ```rust
138//! use ratatui_interact::traits::ClickRegionRegistry;
139//! use ratatui::layout::Rect;
140//!
141//! let mut registry: ClickRegionRegistry<&str> = ClickRegionRegistry::new();
142//!
143//! // Register during render
144//! registry.clear();
145//! registry.register(Rect::new(0, 0, 10, 1), "button1");
146//! registry.register(Rect::new(15, 0, 10, 1), "button2");
147//!
148//! // Check clicks during event handling
149//! if let Some(clicked) = registry.handle_click(5, 0) {
150//!     println!("Clicked: {}", clicked);
151//! }
152//! ```
153
154pub mod components;
155pub mod events;
156pub mod state;
157pub mod traits;
158pub mod utils;
159
160/// Prelude for convenient imports.
161///
162/// Import everything commonly needed:
163///
164/// ```rust
165/// use ratatui_interact::prelude::*;
166/// ```
167pub mod prelude {
168    // Interactive Components
169    pub use crate::components::{
170        Button, ButtonAction, ButtonState, ButtonStyle, ButtonVariant, CheckBox, CheckBoxAction,
171        CheckBoxState, CheckBoxStyle, ContextMenu, ContextMenuAction, ContextMenuItem,
172        ContextMenuState, ContextMenuStyle, DialogConfig, DialogFocusTarget, DialogState, Input,
173        InputAction, InputState, InputStyle, PopupDialog, calculate_menu_height,
174        handle_context_menu_key, handle_context_menu_mouse, is_context_menu_trigger,
175    };
176
177    // Display Components
178    pub use crate::components::{
179        ParagraphExt, Progress, ProgressStyle, Toast, ToastState, ToastStyle,
180    };
181
182    // Navigation Components
183    pub use crate::components::{
184        EntryType, FileEntry, FileExplorer, FileExplorerState, FileExplorerStyle, ListPicker,
185        ListPickerState, ListPickerStyle, key_hints_footer,
186    };
187
188    // Tree Components
189    pub use crate::components::{
190        FlatNode, TreeNode, TreeStyle, TreeView, TreeViewState, get_selected_id,
191    };
192
193    // Layout Components
194    pub use crate::components::{
195        Orientation, SplitPane, SplitPaneAction, SplitPaneState, SplitPaneStyle,
196        handle_split_pane_key, handle_split_pane_mouse,
197    };
198
199    // Viewer Components
200    pub use crate::components::{
201        DiffData, DiffHunk, DiffLine, DiffLineType, DiffViewMode, DiffViewer, DiffViewerAction,
202        DiffViewerState, DiffViewerStyle, LogViewer, LogViewerState, LogViewerStyle, SearchState,
203        Step, StepDisplay, StepDisplayState, StepDisplayStyle, StepStatus, SubStep,
204        handle_diff_viewer_key, handle_diff_viewer_mouse, step_display_height,
205    };
206
207    // Dialog Components
208    pub use crate::components::{
209        CategoryClickRegion, HotkeyCategory, HotkeyClickRegion, HotkeyDialog, HotkeyDialogAction,
210        HotkeyDialogState, HotkeyDialogStyle, HotkeyEntryData, HotkeyFocus, HotkeyProvider,
211        handle_hotkey_dialog_key, handle_hotkey_dialog_mouse, render_hotkey_dialog,
212    };
213
214    // Utilities
215    pub use crate::utils::{
216        clean_for_display, format_size, pad_to_width, parse_ansi_to_spans, truncate_to_width,
217    };
218
219    // Traits
220    pub use crate::traits::{
221        ClickRegion, ClickRegionRegistry, Clickable, Container, ContainerAction, EventResult,
222        FocusId, Focusable, PopupContainer,
223    };
224
225    // State management
226    pub use crate::state::FocusManager;
227
228    // Event helpers
229    pub use crate::events::{
230        get_char, get_mouse_pos, get_scroll, has_alt, has_ctrl, has_shift, is_activate_key,
231        is_backspace, is_backtab, is_close_key, is_ctrl_a, is_ctrl_e, is_ctrl_k, is_ctrl_u,
232        is_ctrl_w, is_delete, is_end, is_enter, is_home, is_left_click, is_navigation_key,
233        is_right_click, is_space, is_tab,
234    };
235}
236
237#[cfg(test)]
238mod tests {
239    use super::prelude::*;
240
241    #[test]
242    fn test_prelude_imports() {
243        // Verify all prelude items are accessible
244        let _: CheckBoxState = CheckBoxState::new(false);
245        let _: InputState = InputState::new("");
246        let _: ButtonState = ButtonState::enabled();
247        let _: FocusManager<usize> = FocusManager::new();
248        let _: ClickRegionRegistry<()> = ClickRegionRegistry::new();
249    }
250}