tui_dispatch_core/
component.rs

1//! Component trait for pure UI elements
2
3use ratatui::{layout::Rect, Frame};
4
5use crate::event::EventKind;
6
7/// A pure UI component that renders based on props and emits actions
8///
9/// Components follow these rules:
10/// 1. Props contain ALL read-only data needed for rendering
11/// 2. `handle_event` returns actions, never mutates external state
12/// 3. `render` is a pure function of props (plus internal UI state like scroll position)
13///
14/// Internal UI state (scroll position, selection highlight) can be stored in `&mut self`,
15/// but data mutations must go through actions.
16///
17/// # Focus and Context
18///
19/// Components receive `EventKind` (the raw event) rather than the full `Event` with context.
20/// Focus information and other context should be passed through `Props`. This keeps components
21/// decoupled from the specific ComponentId type used by the application.
22///
23/// # Example
24///
25/// ```ignore
26/// use tui_dispatch::{Component, EventKind, Frame, Rect};
27///
28/// struct Counter;
29///
30/// struct CounterProps {
31///     count: i32,
32///     is_focused: bool,
33/// }
34///
35/// impl Component<AppAction> for Counter {
36///     type Props<'a> = CounterProps;
37///
38///     fn handle_event(&mut self, event: &EventKind, props: Self::Props<'_>) -> Vec<AppAction> {
39///         if !props.is_focused {
40///             return vec![];
41///         }
42///         if let EventKind::Key(key) = event {
43///             match key.code {
44///                 KeyCode::Up => return vec![AppAction::Increment],
45///                 KeyCode::Down => return vec![AppAction::Decrement],
46///                 _ => {}
47///             }
48///         }
49///         vec![]
50///     }
51///
52///     fn render(&mut self, frame: &mut Frame, area: Rect, props: Self::Props<'_>) {
53///         let text = format!("Count: {}", props.count);
54///         frame.render_widget(Paragraph::new(text), area);
55///     }
56/// }
57/// ```
58pub trait Component<A> {
59    /// Data required to render the component (read-only)
60    type Props<'a>;
61
62    /// Handle an event and return actions to dispatch
63    ///
64    /// Components receive the raw `EventKind` (key press, mouse event, etc.).
65    /// Focus state and other context should be passed through `Props`.
66    ///
67    /// Default implementation returns no actions (render-only components).
68    #[allow(unused_variables)]
69    fn handle_event(&mut self, event: &EventKind, props: Self::Props<'_>) -> Vec<A> {
70        vec![]
71    }
72
73    /// Render the component to the frame
74    fn render(&mut self, frame: &mut Frame, area: Rect, props: Self::Props<'_>);
75}