spair 0.0.9

A framework for single-page application in Rust
Documentation
use std::{cell::Cell, rc::Rc};
use wasm_bindgen::UnwrapThrowExt;

use crate::{
    component::{Comp, Component},
    dom::WsElement,
    queue_render::{val::QueueRender, FnMapC},
};

pub trait AttributeUpdater {
    fn update(&self, name: &str, ws: &WsElement);
}

impl AttributeUpdater for i32 {
    fn update(&self, name: &str, ws: &WsElement) {
        ws.set_attribute(name, *self);
    }
}

impl AttributeUpdater for u32 {
    fn update(&self, name: &str, ws: &WsElement) {
        ws.set_attribute(name, *self);
    }
}

impl AttributeUpdater for f64 {
    fn update(&self, name: &str, ws: &WsElement) {
        ws.set_attribute(name, *self);
    }
}

impl AttributeUpdater for bool {
    fn update(&self, name: &str, ws: &WsElement) {
        ws.set_bool_attribute(name, *self);
    }
}

impl AttributeUpdater for String {
    fn update(&self, name: &str, ws: &WsElement) {
        ws.set_str_attribute(name, self);
    }
}

pub struct QrNormalAttribute {
    element_unmounted: Rc<Cell<bool>>,
    ws_element: WsElement,
    attribute_name: &'static str,
}

impl QrNormalAttribute {
    pub fn new(
        ws_element: WsElement,
        element_unmounted: Rc<Cell<bool>>,
        attribute_name: &'static str,
    ) -> Self {
        Self {
            element_unmounted,
            ws_element,
            attribute_name,
        }
    }
}

impl<T: AttributeUpdater> QueueRender<T> for QrNormalAttribute {
    fn render(&mut self, t: &T) {
        t.update(self.attribute_name, &self.ws_element);
    }

    fn unmounted(&self) -> bool {
        self.element_unmounted.get()
    }
}

pub struct QrNormalAttributeMap<T, U> {
    qra: QrNormalAttribute,
    fn_map: Box<dyn Fn(&T) -> U>,
}

impl<T, U> QrNormalAttributeMap<T, U> {
    pub fn new(qra: QrNormalAttribute, fn_map: Box<dyn Fn(&T) -> U + 'static>) -> Self {
        Self { qra, fn_map }
    }

    fn map(&self, value: &T) -> U {
        (self.fn_map)(value)
    }
}

impl<T, U: AttributeUpdater> QueueRender<T> for QrNormalAttributeMap<T, U> {
    fn render(&mut self, t: &T) {
        let u = self.map(t);
        u.update(self.qra.attribute_name, &self.qra.ws_element)
    }
    fn unmounted(&self) -> bool {
        self.qra.element_unmounted.get()
    }
}

pub struct QrNormalAttributeMapWithState<C, T, U>
where
    C: Component,
{
    qra: QrNormalAttribute,
    comp: Comp<C>,
    fn_map: FnMapC<C, T, U>,
}

impl<C: Component, T, U> QrNormalAttributeMapWithState<C, T, U> {
    pub fn new(qra: QrNormalAttribute, comp: Comp<C>, fn_map: FnMapC<C, T, U>) -> Self {
        Self { qra, comp, fn_map }
    }

    fn map(&self, value: &T) -> U {
        let rc_comp = self.comp.upgrade();
        let comp = rc_comp
            .try_borrow()
            .expect_throw("QrNormalAttributeMapWithState::map::rc_comp.try_borrow().");
        let state = comp.state();
        (self.fn_map)(state, value)
    }
}

impl<C: Component, T, U: AttributeUpdater> QueueRender<T>
    for QrNormalAttributeMapWithState<C, T, U>
{
    fn render(&mut self, t: &T) {
        let u = self.map(t);
        u.update(self.qra.attribute_name, &self.qra.ws_element)
    }
    fn unmounted(&self) -> bool {
        self.qra.element_unmounted.get()
    }
}

type FnWsElementUpdater<T> = Box<dyn Fn(&WsElement, &T)>;

pub struct QrProperty<T> {
    element_unmounted: Rc<Cell<bool>>,
    ws_element: WsElement,
    fn_update: FnWsElementUpdater<T>,
}

impl<T> QrProperty<T> {
    pub fn new(
        ws_element: WsElement,
        element_unmounted: Rc<Cell<bool>>,
        fn_update: FnWsElementUpdater<T>,
    ) -> Self {
        Self {
            element_unmounted,
            ws_element,
            fn_update,
        }
    }
}

impl<T> QueueRender<T> for QrProperty<T> {
    fn render(&mut self, t: &T) {
        (self.fn_update)(&self.ws_element, t);
    }
    fn unmounted(&self) -> bool {
        self.element_unmounted.get()
    }
}

pub struct QrPropertyMap<T, U> {
    qr_property: QrProperty<U>,
    fn_map: Box<dyn Fn(&T) -> U>,
}

impl<T, U> QrPropertyMap<T, U> {
    pub fn new(qr_property: QrProperty<U>, fn_map: Box<dyn Fn(&T) -> U + 'static>) -> Self {
        Self {
            qr_property,
            fn_map,
        }
    }

    fn map(&self, value: &T) -> U {
        (self.fn_map)(value)
    }
}

impl<T, U> QueueRender<T> for QrPropertyMap<T, U> {
    fn render(&mut self, t: &T) {
        let u = self.map(t);
        (self.qr_property.fn_update)(&self.qr_property.ws_element, &u);
    }
    fn unmounted(&self) -> bool {
        self.qr_property.element_unmounted.get()
    }
}

pub struct QrPropertyMapWithState<C, T, U>
where
    C: Component,
{
    qr_property: QrProperty<U>,
    comp: Comp<C>,
    fn_map: FnMapC<C, T, U>,
}

impl<C: Component, T, U> QrPropertyMapWithState<C, T, U> {
    pub fn new(qr_property: QrProperty<U>, comp: Comp<C>, fn_map: FnMapC<C, T, U>) -> Self {
        Self {
            qr_property,
            comp,
            fn_map,
        }
    }

    fn map(&self, value: &T) -> U {
        let rc_comp = self.comp.upgrade();
        let comp = rc_comp
            .try_borrow()
            .expect_throw("QrPropertyMapWithState::map::rc_comp.try_borrow().");
        let state = comp.state();
        (self.fn_map)(state, value)
    }
}

impl<C: Component, T, U> QueueRender<T> for QrPropertyMapWithState<C, T, U> {
    fn render(&mut self, t: &T) {
        let u = self.map(t);
        (self.qr_property.fn_update)(&self.qr_property.ws_element, &u);
    }
    fn unmounted(&self) -> bool {
        self.qr_property.element_unmounted.get()
    }
}

pub struct QrClass {
    element_unmounted: Rc<Cell<bool>>,
    ws_element: WsElement,
    last_class: Option<String>,
}

impl QrClass {
    pub fn new(ws_element: WsElement, element_unmounted: Rc<Cell<bool>>) -> Self {
        Self {
            element_unmounted,
            ws_element,
            last_class: None,
        }
    }

    pub fn update_str(&mut self, value: Option<&str>) {
        let last = self.last_class.as_deref();
        if value.eq(&last) {
            return;
        }
        self.ws_element.remove_class_optional(last);
        self.ws_element.add_class_optional(value);

        self.last_class = value.map(ToString::to_string);
    }

    pub fn update_string(&mut self, value: Option<String>) {
        if value.eq(&self.last_class) {
            return;
        }
        self.ws_element
            .remove_class_optional(self.last_class.as_deref());
        self.ws_element.add_class_optional(value.as_deref());

        self.last_class = value;
    }
}

impl QueueRender<String> for QrClass {
    fn render(&mut self, t: &String) {
        self.update_str(Some(t));
    }
    fn unmounted(&self) -> bool {
        self.element_unmounted.get()
    }
}

impl<'a> QueueRender<&'a str> for QrClass {
    fn render(&mut self, t: &&str) {
        self.update_str(Some(t));
    }
    fn unmounted(&self) -> bool {
        self.element_unmounted.get()
    }
}

impl QueueRender<Option<String>> for QrClass {
    fn render(&mut self, t: &Option<String>) {
        self.update_str(t.as_deref());
    }
    fn unmounted(&self) -> bool {
        self.element_unmounted.get()
    }
}

pub struct QrClassMap<T, U> {
    qr: QrClass,
    fn_map: Box<dyn Fn(&T) -> U>,
}

impl<T, U> QrClassMap<T, U> {
    pub fn new(qr: QrClass, fn_map: Box<dyn Fn(&T) -> U + 'static>) -> Self {
        Self { qr, fn_map }
    }

    fn map(&self, value: &T) -> U {
        (self.fn_map)(value)
    }
}

impl<T> QueueRender<T> for QrClassMap<T, &'static str> {
    fn render(&mut self, t: &T) {
        let u = self.map(t);
        self.qr.update_str(Some(u));
    }
    fn unmounted(&self) -> bool {
        self.qr.element_unmounted.get()
    }
}

impl<T> QueueRender<T> for QrClassMap<T, String> {
    fn render(&mut self, t: &T) {
        let u = self.map(t);
        self.qr.update_string(Some(u));
    }
    fn unmounted(&self) -> bool {
        self.qr.element_unmounted.get()
    }
}

impl<T> QueueRender<T> for QrClassMap<T, Option<String>> {
    fn render(&mut self, t: &T) {
        let t = self.map(t);
        self.qr.update_str(t.as_deref());
    }
    fn unmounted(&self) -> bool {
        self.qr.element_unmounted.get()
    }
}

pub struct QrClassMapWithState<C, T, U>
where
    C: Component,
{
    qr: QrClass,
    comp: Comp<C>,
    fn_map: FnMapC<C, T, U>,
}

impl<C: Component, T, U> QrClassMapWithState<C, T, U> {
    pub fn new(qr: QrClass, comp: Comp<C>, fn_map: FnMapC<C, T, U>) -> Self {
        Self { qr, comp, fn_map }
    }

    fn map(&self, value: &T) -> U {
        let rc_comp = self.comp.upgrade();
        let comp = rc_comp
            .try_borrow()
            .expect_throw("QrClassMapWithState::map::rc_comp.try_borrow().");
        let state = comp.state();
        (self.fn_map)(state, value)
    }
}

impl<C: Component, T> QueueRender<T> for QrClassMapWithState<C, T, &'static str> {
    fn render(&mut self, t: &T) {
        let u = self.map(t);
        self.qr.update_str(Some(u));
    }
    fn unmounted(&self) -> bool {
        self.qr.element_unmounted.get()
    }
}

impl<C: Component, T> QueueRender<T> for QrClassMapWithState<C, T, String> {
    fn render(&mut self, t: &T) {
        let u = self.map(t);
        self.qr.update_string(Some(u));
    }
    fn unmounted(&self) -> bool {
        self.qr.element_unmounted.get()
    }
}

impl<C: Component, T> QueueRender<T> for QrClassMapWithState<C, T, Option<String>> {
    fn render(&mut self, t: &T) {
        let t = self.map(t);
        self.qr.update_str(t.as_deref());
    }
    fn unmounted(&self) -> bool {
        self.qr.element_unmounted.get()
    }
}