skima/web/
mod.rs

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// impl Backend for Json {}
72
73#[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// #[cfg_attr(not(test), wasm_bindgen::prelude::wasm_bindgen(start))]
222// pub fn main() {
223// 	std::panic::set_hook(Box::new(console_error_panic_hook::hook));
224// 	tracing_wasm::set_as_global_default();
225
226// 	let body = web_sys::window()
227// 		.unwrap()
228// 		.document()
229// 		.unwrap()
230// 		.body()
231// 		.unwrap();
232
233// 	let document = web_sys::window().unwrap().document().unwrap();
234// 	let div: HtmlElement = document.create_element("div").unwrap().unchecked_into();
235
236// 	body.append_child(&div).unwrap();
237
238// 	// let mut root = Root::render(fragment(), div);
239// 	let mut root = Root::render(variables(), div);
240
241// 	// root.update(app_list(20..30));
242
243// 	// web::console_log!("Tree {:#?}", &root.tree);
244
245// 	std::mem::forget(root);
246// }