Skip to main content

stratum_core/
event.rs

1use serde::{Deserialize, Serialize};
2
3/// Events that components can receive and handle.
4///
5/// Framework adapters translate framework-specific events (e.g., Leptos `on:click`,
6/// Dioxus `onclick`) into `ComponentEvent` before passing to `Component::on_event`.
7#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
8pub enum ComponentEvent {
9    /// Mouse click event.
10    Click {
11        x: f32,
12        y: f32,
13        button: MouseButton,
14    },
15
16    /// Key pressed down.
17    KeyDown {
18        key: Key,
19        modifiers: ModifierKeys,
20    },
21
22    /// Key released.
23    KeyUp {
24        key: Key,
25        modifiers: ModifierKeys,
26    },
27
28    /// Component received focus.
29    Focus,
30
31    /// Component lost focus.
32    Blur,
33
34    /// Value changed (inputs, selects, etc.).
35    Change {
36        value: String,
37    },
38
39    /// Input event (fires on every keystroke, before Change).
40    Input {
41        value: String,
42    },
43
44    /// Mouse entered the component.
45    PointerEnter,
46
47    /// Mouse left the component.
48    PointerLeave,
49
50    /// Touch start (mobile).
51    TouchStart {
52        x: f32,
53        y: f32,
54    },
55
56    /// Touch end (mobile).
57    TouchEnd,
58
59    /// Custom event for extensibility.
60    Custom {
61        name: String,
62        data: serde_json::Value,
63    },
64}
65
66/// Result of handling an event.
67///
68/// Tells the framework adapter how to handle the event after the component
69/// has processed it, and whether a re-render is needed.
70#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
71pub struct EventResult {
72    /// Whether to call `preventDefault()` on the DOM event.
73    pub prevent_default: bool,
74    /// Whether to call `stopPropagation()` on the DOM event.
75    pub stop_propagation: bool,
76    /// Whether component state changed (triggers re-render).
77    pub state_changed: bool,
78}
79
80impl EventResult {
81    /// Create an EventResult indicating state changed.
82    pub fn state_changed() -> Self {
83        Self {
84            prevent_default: false,
85            stop_propagation: false,
86            state_changed: true,
87        }
88    }
89
90    /// Create an EventResult that prevents default and indicates state changed.
91    pub fn prevent_and_changed() -> Self {
92        Self {
93            prevent_default: true,
94            stop_propagation: false,
95            state_changed: true,
96        }
97    }
98}
99
100/// Mouse button identifier.
101#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
102pub enum MouseButton {
103    Left,
104    Middle,
105    Right,
106}
107
108/// Keyboard key identifier.
109///
110/// Covers all keys needed for ARIA keyboard navigation patterns
111/// as specified in the ARIA Authoring Practices Guide (APG).
112#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
113pub enum Key {
114    Enter,
115    Space,
116    Escape,
117    Tab,
118    ArrowUp,
119    ArrowDown,
120    ArrowLeft,
121    ArrowRight,
122    Home,
123    End,
124    PageUp,
125    PageDown,
126    Backspace,
127    Delete,
128    /// A printable character (for type-ahead in menus, listboxes, etc.).
129    Char(char),
130    /// Function keys.
131    F(u8),
132    /// Any other key not explicitly listed.
133    Other(String),
134}
135
136/// Modifier keys held during a keyboard or mouse event.
137#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
138pub struct ModifierKeys {
139    pub shift: bool,
140    pub ctrl: bool,
141    pub alt: bool,
142    pub meta: bool,
143}
144
145#[cfg(test)]
146mod tests {
147    use super::*;
148
149    #[test]
150    fn event_result_default_is_no_op() {
151        let result = EventResult::default();
152        assert!(!result.prevent_default);
153        assert!(!result.stop_propagation);
154        assert!(!result.state_changed);
155    }
156
157    #[test]
158    fn event_result_state_changed() {
159        let result = EventResult::state_changed();
160        assert!(!result.prevent_default);
161        assert!(result.state_changed);
162    }
163
164    #[test]
165    fn event_result_prevent_and_changed() {
166        let result = EventResult::prevent_and_changed();
167        assert!(result.prevent_default);
168        assert!(result.state_changed);
169    }
170
171    #[test]
172    fn component_event_serialization() {
173        let event = ComponentEvent::Click {
174            x: 10.0,
175            y: 20.0,
176            button: MouseButton::Left,
177        };
178        let json = serde_json::to_string(&event).unwrap();
179        let deserialized: ComponentEvent = serde_json::from_str(&json).unwrap();
180        assert_eq!(event, deserialized);
181    }
182
183    #[test]
184    fn modifier_keys_default() {
185        let mods = ModifierKeys::default();
186        assert!(!mods.shift);
187        assert!(!mods.ctrl);
188        assert!(!mods.alt);
189        assert!(!mods.meta);
190    }
191
192    #[test]
193    fn key_char_variant() {
194        let key = Key::Char('a');
195        assert_eq!(key, Key::Char('a'));
196        assert_ne!(key, Key::Char('b'));
197    }
198}