1use std::ops::Range;
2
3use bump::bump_in;
4pub use callback::Callback;
5use event::{on, on_if};
6use observe::{batch, Var};
7use reactive::reactive;
8
9use self::helpers::cursor::Cursor;
10use crate::{console_log, Backend, Markup};
11
12pub mod api;
13pub mod attr;
14pub mod bump;
15pub mod callback;
16pub mod constant;
17pub mod dispatch;
18pub mod effect;
19pub mod event;
20pub mod helpers;
21pub mod portal;
22pub mod reactive;
23pub mod root;
24pub mod routing;
25pub mod tag;
26pub mod text;
27
28pub mod prelude {
29 pub use crate::reference::reference;
30 pub use crate::web::attr::{attr, classlist, classname};
31 pub use crate::web::event::{on, on_if};
32 pub use crate::web::html::*;
33 pub use crate::web::reactive::reactive;
34 pub use crate::Markup;
35}
36
37pub use attr::*;
38use html::*;
39pub use tag::html;
40
41pub type Target = WebSys;
42
43#[derive(Debug)]
44pub struct WebSys {}
45
46impl WebSys {}
47
48impl Backend for WebSys {
49 type Node = web_sys::Node;
50 type Cursor = Cursor;
51 type Event = web_sys::Event;
52
53 fn replace(node: &Self::Node, prev: &Self::Node) {
54 if let Some(parent_element) = prev.parent_element() {
55 parent_element.replace_child(node, prev).unwrap();
56 }
57 }
58
59 fn insert(cursor: Cursor, node: &Self::Node) {
60 cursor.range.insert_node(node).unwrap()
61 }
62
63 fn cursor_after(node: &Self::Node) -> Self::Cursor {
64 Cursor::after(node).unwrap()
65 }
66
67 fn cursor_beginning_of(node: &Self::Node) -> Self::Cursor {
68 Cursor::beginning_of(node).unwrap()
69 }
70}
71#[derive(Hash, PartialEq)]
74struct Counter(usize);
75
76fn counter(first: usize) -> impl Markup {
77 reactive(move |cx| {
78 cx.with(Counter(first));
79
80 bump_in(cx, |bump| {
81 let str = bumpalo::format!(in bump, "string {}", 10).into_bump_str();
82
83 let counter: &Counter = cx.get();
84 let evenodd = if counter.0 % 2 == 0 { "even" } else { "odd" };
85
86 cx.effect_hash_clean(&evenodd, move |cx| {
87 web_sys::console::log_1(&format!("Run {}", evenodd).into());
88 move |cx| {
89 web_sys::console::log_1(&format!("Cleanup {}", evenodd).into());
90 }
91 });
92
93 div((
94 classname(evenodd),
95 "Counter ",
96 str,
97 counter.0.to_string(),
98 on("click", cx.wrap(|cx, _| cx.update::<Counter>(|c| c.0 += 1))),
99 ))
100 })
101 })
102}
103
104#[derive(Eq, PartialEq)]
105struct ButtonProps {
106 value: usize,
107 on_click: Option<Callback<dyn Fn(usize)>>,
108}
109
110fn button(props: ButtonProps) -> impl Markup {
111 div((
112 "Button",
113 props.value.to_string(),
114 on_if(props.on_click, "click", move |f, _| f(props.value)),
115 ))
116}
117
118fn app1() -> impl Markup {
119 reactive(|cx| {
120 cx.with(0 as usize);
121
122 let on_click = cx.callback_1(|cx, value| {
123 web_sys::console::log_1(&format!("Callback {}", value).into());
124 cx.set(value + 1);
125 });
126
127 button(ButtonProps {
128 value: *cx.get(),
129 on_click: Some(on_click),
130 })
131 })
132}
133
134fn wrapper(children: impl Markup) -> impl Markup {
135 div(children)
136}
137
138fn app2() -> impl Markup {
139 wrapper("String")
140}
141
142struct A(bool);
143struct B(bool);
144struct C(bool);
145struct D(bool);
146struct E(bool);
147
148fn fragments() -> impl Markup {
149 reactive(|cx| {
150 cx.with(A(false));
151 cx.with(B(false));
152 cx.with(C(false));
153 cx.with(D(false));
154
155 let a = cx.get::<A>();
156 let b = cx.get::<B>();
157 let c = cx.get::<C>();
158 let d = cx.get::<D>();
159
160 (
161 a.0.then(|| ("a", "a", "a", classname("a"))),
162 b.0.then(|| ("b", "b", "b")),
163 c.0.then(|| ("c", "c", "c")),
164 d.0.then(|| ("d", "d", "d")),
165 (
166 input((
167 attr("type", "checkbox"),
168 on("change", cx.wrap(|cx, _| cx.set::<A>(A(!cx.get::<A>().0)))),
169 )),
170 input((
171 attr("type", "checkbox"),
172 on("change", cx.wrap(|cx, _| cx.set::<B>(B(!cx.get::<B>().0)))),
173 )),
174 input((
175 attr("type", "checkbox"),
176 on("change", cx.wrap(|cx, _| cx.set::<C>(C(!cx.get::<C>().0)))),
177 )),
178 input((
179 attr("type", "checkbox"),
180 on("change", cx.wrap(|cx, _| cx.set::<D>(D(!cx.get::<D>().0)))),
181 )),
182 ),
183 )
184 })
185}
186
187pub fn app_list(range: Range<usize>) -> impl Markup {
188 crate::list::list(range.map(|i| (i, i)), |i, _| (span(i.to_string()), br(())))
189}
190
191fn variables() -> impl Markup {
192 reactive(|cx| {
193 cx.with(Var::new(true));
194
195 let var = cx.get::<Var<bool>>();
196 let text = if *var.get(cx) == true {
197 "True"
198 } else {
199 "False"
200 };
201
202 cx.effect_hash_clean(text, |cx| {
203 console_log!("Effect");
204 |cx| {
205 console_log!("Effect cleanup");
206 }
207 });
208
209 (
210 text,
211 on(
212 "click",
213 cx.wrap(|cx, _e| {
214 batch(|| cx.get::<Var<bool>>().update(|v| *v = !*v));
215 }),
216 ),
217 )
218 })
219}
220
221