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
use std::fmt::Display;

use crate::effects::attr_mixin::XEffect;
use crate::Dom;
use hirola_core::effect::SideEffect;
use hirola_core::prelude::signal::{DedupeCloned, DedupeMap};
use hirola_core::prelude::EffectAttribute;
use hirola_core::{
    generic_node::GenericNode,
    prelude::signal::{Signal, SignalExt},
};
use hirola_macros::mixin;
use wasm_bindgen::JsCast;
use web_sys::Element;

/// A mixin that allows adding non-signal text
#[allow(unused_variables)]
#[mixin]
pub fn raw_text<'a>(text: &'a str) -> Box<dyn Fn(&Dom) + 'a> {
    let cb = move |dom: &Dom| {
        dom.node.set_text_content(Some(text));
    };
    Box::new(cb)
}

pub struct Html;

fn html<'a>(text: &'a str) -> Box<dyn Fn(&Dom) + 'a> {
    let cb = move |node: &Dom| {
        let dom = node.inner_element();
        let element = dom.dyn_ref::<Element>().unwrap();
        element.set_inner_html(text); // Remember to escape this.
    };
    Box::new(cb)
}

impl EffectAttribute for Html {
    type Handler = XEffect;
    fn read_as_attr(&self) -> String {
        "html".to_owned()
    }
}

impl SideEffect<Html, &str, Dom> for XEffect {
    fn effect(&self, node: &Dom, _: Html, text: &str) {
        html(text)(node)
    }
}

pub struct Text;

impl EffectAttribute for Text {
    type Handler = XEffect;
    fn read_as_attr(&self) -> String {
        "text".to_owned()
    }
}

impl<
        F: FnMut(&mut <S as Signal>::Item) -> A + 'static,
        S: Signal + 'static,
        A: Display + 'static + Clone + PartialEq,
    > SideEffect<Text, DedupeMap<S, F>, Dom> for XEffect
where
    <S as Signal>::Item: PartialEq,
{
    fn effect(&self, node: &Dom, _attr: Text, effect: DedupeMap<S, F>) {
        let dom = node.clone();
        let element = dom.dyn_into::<Element>().unwrap();
        let future = SignalExt::dedupe_map(effect, move |value| {
            element.set_text_content(Some(&format!("{}", value)));
        })
        .to_future();
        node.effect(future);
    }
}

impl<S: Signal + 'static> SideEffect<Text, DedupeCloned<S>, Dom> for XEffect
where
    <S as Signal>::Item: PartialEq + Display + Clone,
{
    fn effect(&self, node: &Dom, _attr: Text, effect: DedupeCloned<S>) {
        let dom = node.clone();
        let element = dom.dyn_into::<Element>().unwrap();
        let future = SignalExt::dedupe_map(effect, move |value| {
            element.set_text_content(Some(&format!("{}", value)));
        })
        .to_future();
        node.effect(future);
    }
}