yew_state/component/
view.rs

1use std::rc::Rc;
2
3use yew::{Component, ComponentLink, Html, Properties, ShouldRender};
4
5use crate::handle::Handle;
6use crate::{SharedState, SharedStateComponent};
7
8pub type Render<H> = Rc<dyn Fn(&H) -> Html>;
9pub type Rendered<H> = Rc<dyn Fn(&H, bool)>;
10pub type Change<H> = Rc<dyn Fn(&H, &H) -> bool>;
11
12#[derive(Properties, Clone)]
13pub struct Props<H>
14where
15    H: Handle + Clone + Default,
16{
17    #[prop_or_default]
18    handle: H,
19    pub view: Render<H>,
20    #[prop_or_default]
21    pub rendered: Option<Rendered<H>>,
22    #[prop_or_default]
23    pub change: Option<Change<H>>,
24}
25
26impl<H> SharedState for Props<H>
27where
28    H: Handle + Clone + Default,
29{
30    type Handle = H;
31
32    fn handle(&mut self) -> &mut Self::Handle {
33        &mut self.handle
34    }
35}
36
37pub enum Msg {}
38
39pub struct Model<H>
40where
41    H: Handle + Clone + Default,
42{
43    props: Props<H>,
44}
45
46impl<H> Component for Model<H>
47where
48    H: Handle + Default + Clone + 'static,
49{
50    type Message = Msg;
51    type Properties = Props<H>;
52
53    fn create(props: Self::Properties, _link: ComponentLink<Self>) -> Self {
54        Self { props }
55    }
56
57    fn rendered(&mut self, first_render: bool) {
58        if let Some(ref f) = self.props.rendered {
59            f(&self.props.handle, first_render)
60        }
61    }
62
63    fn update(&mut self, _msg: Self::Message) -> ShouldRender {
64        true
65    }
66
67    fn view(&self) -> Html {
68        (self.props.view)(&self.props.handle)
69    }
70
71    fn change(&mut self, props: Self::Properties) -> ShouldRender {
72        // Check if other property functions have changed
73        let is_eq = Rc::ptr_eq(&self.props.view, &props.view)
74            && ptr_eq(&self.props.rendered, &props.rendered)
75            && ptr_eq(&self.props.change, &props.change);
76        // Update functions if they changed.
77        if !is_eq {
78            self.props.view = props.view;
79            self.props.rendered = props.rendered;
80            self.props.change = props.change;
81        }
82        // Check if state should be updated.
83        let should_change = {
84            if let Some(ref f) = self.props.change {
85                f(&self.props.handle, &props.handle)
86            } else {
87                // Should change by default.
88                true
89            }
90        };
91        // Update state if desired.
92        if should_change {
93            self.props.handle = props.handle;
94        }
95
96        !is_eq || should_change
97    }
98}
99
100fn ptr_eq<T: ?Sized>(a: &Option<Rc<T>>, b: &Option<Rc<T>>) -> bool {
101    a.as_ref()
102        .zip(b.as_ref())
103        .map(|(a, b)| Rc::ptr_eq(a, b))
104        .unwrap_or_default()
105}
106
107pub type StateView<H> = SharedStateComponent<Model<H>>;
108
109/// Wraps `f` in `Rc`. Helps with resolving type needed for view property.
110pub fn view<F, H>(f: F) -> Render<H>
111where
112    F: Fn(&H) -> Html + 'static,
113{
114    Rc::new(f)
115}
116
117/// Wraps `f` in `Rc`. Helps with resolving type needed for rendered property.
118pub fn rendered<F, H>(f: F) -> Rendered<H>
119where
120    F: Fn(&H, bool) + 'static,
121{
122    Rc::new(f)
123}
124
125/// Wraps `f` in `Rc`. Helps with resolving type needed for rendered property.
126pub fn change<F, H>(f: F) -> Change<H>
127where
128    F: Fn(&H, &H) -> bool + 'static,
129{
130    Rc::new(f)
131}