use std::rc::Rc;
use yew::{Component, ComponentLink, Html, Properties, ShouldRender};
use crate::{
component::wrapper::WithDispatch,
dispatch::{DispatchProps, DispatchPropsMut},
store::Store,
};
pub type Render<STORE> = Rc<dyn Fn(&DispatchProps<STORE>) -> Html>;
pub type Rendered<STORE> = Rc<dyn Fn(&DispatchProps<STORE>, bool)>;
pub type Change<STORE> = Rc<dyn Fn(&DispatchProps<STORE>, &DispatchProps<STORE>) -> bool>;
#[derive(Properties, Clone)]
pub struct Props<STORE>
where
STORE: Store + Clone + Default,
{
#[prop_or_default]
dispatch: DispatchProps<STORE>,
pub view: Render<STORE>,
#[prop_or_default]
pub rendered: Option<Rendered<STORE>>,
#[prop_or_default]
pub change: Option<Change<STORE>>,
}
impl<STORE> DispatchPropsMut for Props<STORE>
where
STORE: Store + Clone + Default,
{
type Store = STORE;
fn dispatch(&mut self) -> &mut DispatchProps<Self::Store> {
&mut self.dispatch
}
}
pub enum Msg {}
pub struct Model<STORE>
where
STORE: Store + Clone + Default,
{
props: Props<STORE>,
}
impl<STORE> Component for Model<STORE>
where
STORE: Store + Default + Clone + 'static,
{
type Message = Msg;
type Properties = Props<STORE>;
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.dispatch, first_render)
}
}
fn update(&mut self, _msg: Self::Message) -> ShouldRender {
true
}
fn view(&self) -> Html {
(self.props.view)(&self.props.dispatch)
}
fn change(&mut self, props: Self::Properties) -> ShouldRender {
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);
if !is_eq {
self.props.view = props.view;
self.props.rendered = props.rendered;
self.props.change = props.change;
}
let should_change = {
if let Some(ref f) = self.props.change {
f(&self.props.dispatch, &props.dispatch)
} else {
true
}
};
if should_change {
self.props.dispatch = props.dispatch;
}
!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> = WithDispatch<Model<H>>;
pub fn view<F, STORE>(f: F) -> Render<STORE>
where
STORE: Store,
F: Fn(&DispatchProps<STORE>) -> Html + 'static,
{
Rc::new(f)
}
pub fn rendered<F, STORE>(f: F) -> Rendered<STORE>
where
STORE: Store,
F: Fn(&DispatchProps<STORE>, bool) + 'static,
{
Rc::new(f)
}
pub fn change<F, STORE>(f: F) -> Change<STORE>
where
STORE: Store,
F: Fn(&DispatchProps<STORE>, &DispatchProps<STORE>) -> bool + 'static,
{
Rc::new(f)
}