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}