hirola-dom 0.4.0

Hirola is an un-opinionated web framework that is focused on simplicity and predictability
Documentation
pub mod prelude {
    use super::*;
    pub use attr_bind::*;
    pub use attr_mixin::*;
    pub use attr_on::*;
    pub use attr_use::*;
}

pub mod attr_use {
    use std::fmt::Display;

    use hirola_core::{
        effect::EffectAttribute,
        prelude::{signal::SignalExt, GenericNode, SideEffect},
    };
    pub struct UseEffect;

    pub struct Future;

    impl EffectAttribute for Future {
        type Handler = UseEffect;
        fn read_as_attr(&self) -> String {
            "future".to_owned()
        }
    }
    impl<F: std::future::Future<Output = ()> + 'static, N: GenericNode> SideEffect<Future, F, N>
        for UseEffect
    {
        fn effect(&self, node: &N, _marker: Future, future: F) {
            node.effect(future)
        }
    }

    pub struct SignalAttr<S: hirola_core::prelude::signal::Signal>(String, S);

    pub fn attr_signal<S: hirola_core::prelude::signal::Signal>(
        attr: &str,
        signal: S,
    ) -> SignalAttr<S> {
        SignalAttr(attr.to_owned(), signal)
    }

    pub struct Signal;

    impl EffectAttribute for Signal {
        type Handler = UseEffect;
        fn read_as_attr(&self) -> String {
            "signal".to_owned()
        }
    }

    impl<
            S: hirola_core::prelude::signal::Signal<Item = D> + 'static,
            D: Display + PartialEq,
            N: GenericNode,
        > SideEffect<Signal, SignalAttr<S>, N> for UseEffect
    {
        fn effect(&self, dom: &N, _: Signal, signal: SignalAttr<S>) {
            let node = dom.clone();
            let attr = signal.0;
            let future = SignalExt::dedupe_map(signal.1, move |value| {
                GenericNode::set_attribute(&node, &attr, &value.to_string());
            })
            .to_future();

            dom.effect(future);
        }
    }
}

pub mod attr_on {
    pub struct OnEffect;

    use hirola_core::{
        effect::{EffectAttribute, SideEffect},
        generic_node::{EventListener, GenericNode},
    };
    use web_sys::Event;

    pub use crate::types::DomEvent::*;
    impl<
            D: EffectAttribute<Handler = OnEffect>,
            F: Fn(Event) + 'static,
            N: GenericNode + EventListener<F>,
        > SideEffect<D, F, N> for OnEffect
    {
        fn effect(&self, node: &N, attr: D, effect: F) {
            node.event(&attr.read_as_attr(), effect)
        }
    }
}

pub mod attr_bind {
    pub struct Value;
    use std::{
        fmt::{Debug, Display},
        str::FromStr,
    };

    use hirola_core::{
        effect::{EffectAttribute, SideEffect},
        generic_node::{EventListener, GenericNode, NodeReference},
        prelude::{signal::SignalExt, Mutable},
    };
    use wasm_bindgen::JsCast;
    use web_sys::{Event, HtmlInputElement};

    use crate::{node_ref::NodeRef, Dom};
    impl EffectAttribute for Value {
        type Handler = BindEffect;
        fn read_as_attr(&self) -> String {
            "value".to_owned()
        }
    }

    pub struct BindEffect;

    impl EffectAttribute for Ref {
        type Handler = BindEffect;
        fn read_as_attr(&self) -> String {
            "ref".to_owned()
        }
    }

    pub struct Ref;

    impl SideEffect<Ref, NodeRef, Dom> for BindEffect {
        fn effect(&self, node: &Dom, _attr: Ref, value: NodeRef) {
            NodeReference::set(&value, node.clone());
        }
    }

    impl<
            T: Clone + Display + PartialEq + 'static + FromStr,
            D: EffectAttribute<Handler = BindEffect>,
            N: GenericNode + EventListener<Box<dyn Fn(Event)>>,
        > SideEffect<D, &Mutable<T>, N> for BindEffect
    where
        <T as FromStr>::Err: Debug,
    {
        fn effect(&self, node: &N, attr: D, value: &Mutable<T>) {
            let attr = attr.read_as_attr().to_owned();
            let dom = node.clone();
            let future = SignalExt::dedupe_map(value.signal_cloned(), move |value| {
                GenericNode::set_attribute(&dom, &attr, &format!("{}", value));
            })
            .to_future();
            let value = value.clone();
            let handler = move |e: Event| {
                let input = e
                    .current_target()
                    .unwrap()
                    .dyn_into::<HtmlInputElement>()
                    .unwrap();
                let new_value = input.value();
                value.set(new_value.parse().unwrap());
            };
            node.event("input", Box::new(handler));
            node.effect(future);
        }
    }
}

pub mod attr_mixin {
    pub struct XEffect;

    use hirola_core::effect::{EffectAttribute, SideEffect};
    pub use XEffect as MixinEffect;

    use crate::Dom;

    pub use crate::mixins::*;

    impl<A: EffectAttribute<Handler = XEffect>> SideEffect<A, Box<dyn FnOnce(&Dom)>, Dom> for XEffect {
        fn effect(&self, node: &Dom, _: A, effect: Box<dyn FnOnce(&Dom)>) {
            effect(node);
        }
    }
}