rosace_core/component.rs
1use crate::context::Context;
2use crate::element::Element;
3use crate::types::ComponentId;
4
5/// The core trait every ROSACE component implements.
6///
7/// A component is a pure function from props (`&self`) and [`Context`] to an
8/// [`Element`] tree. The framework calls `build` every frame (Phase 1) or when
9/// the component is marked dirty by a state change (Phase 2+).
10///
11/// # Example
12/// ```rust,ignore
13/// struct Greeting { name: String }
14///
15/// impl Component for Greeting {
16/// fn build(&self, ctx: &mut Context) -> Element {
17/// Text::new(format!("Hello, {}!", self.name)).into_element()
18/// }
19/// }
20/// ```
21pub trait Component: Send + Sync + 'static {
22 /// Produce the element tree for this component.
23 fn build(&self, ctx: &mut Context) -> Element;
24
25 /// Called once after the component first appears in the tree.
26 ///
27 /// Default implementation is a no-op. Override for side effects that
28 /// should run on mount (e.g., starting timers, subscriptions).
29 fn on_mount(&self) {}
30
31 /// Called once after the component is removed from the tree.
32 ///
33 /// Default implementation is a no-op. Override for cleanup that cannot
34 /// be expressed as a `ctx.on_cleanup` closure (e.g., releasing platform
35 /// resources held by `self`).
36 fn on_unmount(&self) {}
37
38 /// Fully-qualified type name used in diagnostics.
39 fn type_name(&self) -> &'static str {
40 std::any::type_name::<Self>()
41 }
42
43 /// Convert this component into an [`Element`] so it can be embedded
44 /// inside another component's `build()` output.
45 ///
46 /// The reconciler assigns the real position-based [`ComponentId`] during
47 /// the tree walk; `ComponentId(0)` here is a placeholder.
48 fn into_element(self) -> Element
49 where
50 Self: Sized,
51 {
52 use crate::element::{ComponentElement, Element};
53 use std::sync::Arc;
54
55 Element::Component(ComponentElement {
56 id: ComponentId(0),
57 key: None,
58 component: Arc::new(self),
59 children: vec![],
60 })
61 }
62}