1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
use std::rc::Rc;

use yew::{Component, ComponentLink, Html, Properties, ShouldRender};

use crate::handle::Handle;
use crate::{SharedState, SharedStateComponent};

pub type Render<H> = Rc<dyn Fn(&H) -> Html>;
pub type Rendered<H> = Rc<dyn Fn(&H, bool)>;
pub type Change<H> = Rc<dyn Fn(&H, &H) -> bool>;

#[derive(Properties, Clone)]
pub struct Props<H>
where
    H: Handle + Clone + Default,
{
    #[prop_or_default]
    handle: H,
    pub view: Render<H>,
    #[prop_or_default]
    pub rendered: Option<Rendered<H>>,
    #[prop_or_default]
    pub change: Option<Change<H>>,
}

impl<H> SharedState for Props<H>
where
    H: Handle + Clone + Default,
{
    type Handle = H;

    fn handle(&mut self) -> &mut Self::Handle {
        &mut self.handle
    }
}

pub enum Msg {}

pub struct Model<H>
where
    H: Handle + Clone + Default,
{
    props: Props<H>,
}

impl<H> Component for Model<H>
where
    H: Handle + Default + Clone + 'static,
{
    type Message = Msg;
    type Properties = Props<H>;

    fn create(props: Self::Properties, _link: ComponentLink<Self>) -> Self {
        Self { props }
    }

    fn rendered(&mut self, first_render: bool) {
        if let Some(ref f) = self.props.rendered {
            f(&self.props.handle, first_render)
        }
    }

    fn update(&mut self, _msg: Self::Message) -> ShouldRender {
        true
    }

    fn view(&self) -> Html {
        (self.props.view)(&self.props.handle)
    }

    fn change(&mut self, props: Self::Properties) -> ShouldRender {
        // Check if other property functions have changed
        let is_eq = Rc::ptr_eq(&self.props.view, &props.view)
            && ptr_eq(&self.props.rendered, &props.rendered)
            && ptr_eq(&self.props.change, &props.change);
        // Update functions if they changed.
        if !is_eq {
            self.props.view = props.view;
            self.props.rendered = props.rendered;
            self.props.change = props.change;
        }
        // Check if state should be updated.
        let should_change = {
            if let Some(ref f) = self.props.change {
                f(&self.props.handle, &props.handle)
            } else {
                // Should change by default.
                true
            }
        };
        // Update state if desired.
        if should_change {
            self.props.handle = props.handle;
        }

        !is_eq || should_change
    }
}

fn ptr_eq<T: ?Sized>(a: &Option<Rc<T>>, b: &Option<Rc<T>>) -> bool {
    a.as_ref()
        .zip(b.as_ref())
        .map(|(a, b)| Rc::ptr_eq(a, b))
        .unwrap_or_default()
}

pub type StateView<H, SCOPE = <H as Handle>::Handler> = SharedStateComponent<Model<H>, SCOPE>;

/// Wraps `f` in `Rc`. Helps with resolving type needed for view property.
pub fn view<F, H>(f: F) -> Render<H>
where
    F: Fn(&H) -> Html + 'static,
{
    Rc::new(f)
}

/// Wraps `f` in `Rc`. Helps with resolving type needed for rendered property.
pub fn rendered<F, H>(f: F) -> Rendered<H>
where
    F: Fn(&H, bool) + 'static,
{
    Rc::new(f)
}

/// Wraps `f` in `Rc`. Helps with resolving type needed for rendered property.
pub fn change<F, H>(f: F) -> Change<H>
where
    F: Fn(&H, &H) -> bool + 'static,
{
    Rc::new(f)
}