rust_fel/component.rs
1use crate::element::Element;
2
3/// ```Components``` are the basic building blocks of the UI. Each ```Component```
4/// chooses how to display itself using received props and self-managed state.
5/// Inspired by [Yew](https://github.com/yewstack/yew)'s [Component](https://docs.rs/yew/0.17.3/yew/html/trait.Component.html)
6/// # Examples
7/// A rust_fel [struct](https://doc.rust-lang.org/std/keyword.struct.html) component implements [rust_fel::Component](../rust_fel/trait.Component.html)
8///```ignore
9///use crate::action::Action;
10///use crate::handle;
11///use crate::main_child::{ChildProps, MainChild};
12///use std::cell::RefCell;
13///use std::rc::Rc;
14///use wasm_bindgen::JsCast;
15///
16///#[derive(Debug, Default, Clone)]
17///pub struct MainState {
18/// count: i32,
19///}
20///
21///pub enum Actions {
22/// Counter(Action),
23///}
24///
25///#[derive(Debug, Default, Clone)]
26///pub struct Main {
27/// child: handle::Handle<MainChild>,
28/// id: String,
29/// state: MainState,
30/// props: String,
31///}
32///
33///impl Main {
34/// pub fn create() -> handle::Handle<Self> {
35/// let main = Main {
36/// id: "main".to_owned(),
37/// state: MainState {
38/// count: 0,
39/// },
40/// child: MainChild::create(),
41/// ..Default::default()
42/// };
43/// handle::Handle(Rc::new(RefCell::new(main)))
44/// }
45///}
46///
47///impl rust_fel::Component for handle::Handle<Main> {
48/// type Properties = String;
49/// type Message = Actions;
50/// type State = MainState;
51///
52/// fn add_props(&mut self, props: Self::Properties) {
53/// self.0.borrow_mut().props = props;
54/// }
55///
56/// fn reduce_state(&mut self, message: Actions) {
57/// match message {
58/// Actions::Counter(Action::Increment) => self.0.borrow_mut().state.count += 100,
59/// Actions::Counter(Action::Decrement) => self.0.borrow_mut().state.count -= 100,
60/// }
61///
62/// rust_fel::re_render(self.render(), Some(self.0.borrow().id.clone()));
63/// }
64///
65/// fn render(&self) -> rust_fel::Element {
66/// let mut clone_for_props_closure = self.clone();
67/// let mut clone_for_inc = self.clone();
68/// let mut borrow = self.0.borrow_mut();
69/// let state = borrow.state.clone();
70/// let props_closure = Rc::new(RefCell::new(move || {
71/// clone_for_props_closure.reduce_state(Actions::Counter(Action::Decrement))
72/// }));
73///
74/// let child_props = ChildProps {
75/// counter_props: state.count.to_string(),
76/// closure: Some(props_closure),
77/// };
78///
79/// borrow.child.add_props(child_props);
80///
81/// let main_text = rust_fel::html(format!(
82/// "<span | data-cy=main-text| >Main {}</span>",
83/// state.count.to_string()
84/// ));
85///
86/// let inc_button = rust_fel::Element::new(
87/// "button".to_owned(),
88/// rust_fel::Props {
89/// text: Some("Increment".to_owned()),
90/// on_click: Some(Box::new(move || {
91/// clone_for_inc.reduce_state(Actions::Counter(Action::Increment))
92/// })),
93/// data_cy: Some("increment-main".to_owned()),
94/// children: Some(vec![inc_button_text]),
95/// ..Default::default()
96/// },
97/// );
98///
99/// let main_el = rust_fel::Element::new(
100/// "div".to_owned(),
101/// rust_fel::Props {
102/// class_name: Some("main-el".to_owned()),
103/// children: Some(vec![main_text, inc_button, input_wrapper]),
104/// ..Default::default()
105/// },
106/// );
107///
108/// let child_wrapper = rust_fel::Element::new(
109/// "div".to_owned(),
110/// rust_fel::Props {
111/// class_name: Some("child-wrapper".to_owned()),
112/// children: Some(vec![borrow.child.render()]),
113/// ..Default::default()
114/// },
115/// );
116///
117/// rust_fel::Element::new(
118/// "div".to_owned(),
119/// rust_fel::Props {
120/// id: Some(borrow.id.clone()),
121/// class_name: Some("main".to_owned()),
122/// children: Some(vec![main_el, child_wrapper]),
123/// ..Default::default()
124/// },
125/// )
126/// }
127///}
128///
129///```
130pub trait Component: Sized + 'static {
131 /// Messages are used to make ```Components``` dynamic and interactive.
132 type Message: 'static;
133
134 /// ```Properties``` are the inputs to a ```Component``` and should not be mutated.
135 type Properties: 'static;
136
137 /// A ```Component's``` internal state.
138 type State: 'static;
139
140 /// Construct your html view here.
141 fn render(&self) -> Element;
142
143 /// How a ```Component manages``` internal state.
144 fn reduce_state(&mut self, message: Self::Message);
145
146 /// Invoked by a ```Component's``` parent in order to pass properties.
147 fn add_props(&mut self, props: Self::Properties);
148}