1#![allow(non_snake_case, non_upper_case_globals)]
3
4use anyhow::anyhow;
5use gloo::events::EventListener;
6use js_sys::wasm_bindgen::*;
7use kano::platform::Platform;
8use wasm_bindgen::prelude::*;
9use web_sys::{window, Document};
10use web_sys::{Element, EventTarget};
11
12use kano::{Diff, Event, On, View};
13
14pub mod html;
15
16mod element;
17
18#[cfg(feature = "web-component")]
19pub mod web_component;
20
21mod js {
22 use super::*;
23
24 #[wasm_bindgen]
25 extern "C" {
26 #[wasm_bindgen(js_namespace = console)]
29 pub fn log(s: &str);
30 }
31}
32
33pub struct Web {}
34
35impl Platform for Web {
36 type Cursor = WebCursor;
37
38 fn run_app<V: View<Self>, F: (FnOnce() -> V) + 'static>(func: F) -> anyhow::Result<()> {
39 console_error_panic_hook::set_once();
40
41 let mut cursor = WebCursor::Detached;
42 let state = kano::view::Func(func, ()).init(&mut cursor);
43
44 let WebCursor::Node(node, _) = cursor else {
45 return Err(anyhow!("No node rendered"));
46 };
47
48 document()
49 .body()
50 .unwrap()
51 .append_child(&node)
52 .map_err(|e| anyhow!("{e:?}"))?;
53
54 std::mem::forget(state);
56 Ok(())
57 }
58
59 fn log(s: &str) {
60 js::log(s);
61 }
62
63 fn spawn_task(task: impl std::future::Future<Output = ()> + 'static) {
64 wasm_bindgen_futures::spawn_local(task);
65 }
66}
67
68#[derive(Clone, Debug)]
69pub enum WebCursor {
70 Detached,
71 Node(web_sys::Node, Mode),
72 AfterLastChild(web_sys::Element, Mode),
73 AttrsOf(web_sys::Element, Mode),
74}
75
76#[derive(Clone, Copy, Debug)]
77pub enum Mode {
78 Append,
79 Diff,
80}
81
82impl WebCursor {
83 fn mode(&self) -> Mode {
84 match self {
85 WebCursor::AttrsOf(_, mode) => *mode,
86 WebCursor::AfterLastChild(_, mode) => *mode,
87 WebCursor::Node(_, mode) => *mode,
88 WebCursor::Detached => Mode::Append,
89 }
90 }
91}
92
93impl WebCursor {
94 fn element(&mut self, tag: &str) -> web_sys::Node {
95 match self.mode() {
96 Mode::Append => {
97 let element = document().create_element(tag).unwrap();
98 self.append_node(&element);
99 element.into()
101 }
102 Mode::Diff => match self {
103 Self::Node(..) => self.handle(),
104 _ => panic!(),
105 },
106 }
107 }
108
109 fn enter_attrs(&mut self) {
110 match self {
111 WebCursor::Node(node, mode) => {
112 if let Some(element) = node.dyn_ref::<web_sys::Element>() {
113 *self = WebCursor::AttrsOf(element.clone(), *mode);
114 } else {
115 panic!("Non-element attributes");
116 }
117 }
118 WebCursor::AfterLastChild(..) => {
119 panic!("Entering attrs of empty children");
120 }
121 WebCursor::AttrsOf(..) | WebCursor::Detached => panic!(),
122 }
123 }
124
125 fn exit_attrs(&mut self) {
126 match self {
127 WebCursor::AttrsOf(element, mode) => {
128 *self = WebCursor::Node(element.dyn_ref::<web_sys::Node>().unwrap().clone(), *mode);
129 }
130 WebCursor::AfterLastChild(..) => panic!(),
131 WebCursor::Node(..) => panic!(),
132 WebCursor::Detached => panic!(),
133 }
134 }
135}
136
137impl kano::platform::Cursor for WebCursor {
138 type TextHandle = web_sys::Node;
139 type EventHandle = gloo::events::EventListener;
140
141 fn from_text_handle(handle: &web_sys::Node) -> Self {
142 Self::Node(handle.clone(), Mode::Append)
143 }
144
145 fn empty(&mut self) {
146 match &self {
147 WebCursor::AttrsOf(..) => {}
148 _ => {
149 let comment = document().create_comment("");
150 self.append_node(&comment);
151 }
152 }
153 }
154
155 fn text(&mut self, text: &str) -> web_sys::Node {
156 match self.mode() {
157 Mode::Append => {
158 let text_node = document().create_text_node(text);
159 self.append_node(&text_node);
160 text_node.into()
161 }
162 Mode::Diff => {
163 self.update_text(text);
164 self.handle()
165 }
166 }
167 }
168
169 fn update_text(&mut self, text: &str) {
170 match self {
171 Self::Node(node, _) => {
172 node.set_node_value(Some(text));
173 }
174 _ => panic!(),
175 }
176 }
177
178 fn on_event(&mut self, on_event: On) -> EventListener {
179 match self {
180 WebCursor::AttrsOf(element, _mode) => {
181 Web::log("on_event");
182 let event_target: &EventTarget = element.dyn_ref().unwrap();
183 let event_type = match on_event.event() {
184 Event::Click => "click",
185 Event::MouseOver => "mouseover",
186 };
187
188 EventListener::new(&event_target, event_type, move |_| {
189 on_event.invoke();
190 })
191 }
192 WebCursor::Node(..) => panic!(),
193 WebCursor::Detached => panic!(),
194 WebCursor::AfterLastChild(..) => panic!(),
195 }
196 }
197
198 fn enter_children(&mut self) {
199 match self {
200 WebCursor::Node(node, mode) => {
201 if let Some(child) = node.first_child() {
202 *self = WebCursor::Node(child, *mode);
204 } else if let Some(element) = node.dyn_ref::<web_sys::Element>() {
205 *self = WebCursor::AfterLastChild(element.clone(), *mode);
207 } else {
208 panic!("No children");
209 }
210 }
211 WebCursor::AfterLastChild(_, _) | WebCursor::Detached => {
212 panic!("Enter empty children");
213 }
214 WebCursor::AttrsOf(_, _) => {}
215 }
216 }
217
218 fn exit_children(&mut self) {
219 match self {
221 WebCursor::Node(node, mode) => {
222 let parent = node.parent_element().unwrap();
223 *self = WebCursor::Node(parent.dyn_into().unwrap(), *mode);
224 }
225 WebCursor::AfterLastChild(element, mode) => {
226 *self = WebCursor::Node(element.dyn_ref::<web_sys::Node>().unwrap().clone(), *mode);
227 }
228 WebCursor::AttrsOf(..) => {}
229 WebCursor::Detached => panic!("no children"),
230 }
231 }
232
233 fn next_sibling(&mut self) {
234 match &self {
235 WebCursor::AfterLastChild(..) => {}
236 WebCursor::Detached => panic!(),
237 WebCursor::AttrsOf(..) => {
238 }
240 WebCursor::Node(node, mode) => {
241 if let Some(next) = node.next_sibling() {
242 *self = WebCursor::Node(next, *mode);
243 } else {
244 let parent = node.parent_element().unwrap();
245 *self = WebCursor::AfterLastChild(parent, *mode);
246 }
247 }
248 }
249 }
250
251 fn remove(&mut self) {
252 match &self {
253 WebCursor::AfterLastChild(..) => panic!(),
254 WebCursor::Detached => panic!(),
255 WebCursor::AttrsOf(..) => {
256 todo!()
257 }
258 WebCursor::Node(node, mode) => {
259 let next = if let Some(next) = node.next_sibling() {
260 WebCursor::Node(next, *mode)
261 } else {
262 let parent = node.parent_element().unwrap();
263 WebCursor::AfterLastChild(parent, *mode)
264 };
265
266 if let Some(element) = node.dyn_ref::<Element>() {
267 element.remove();
268 } else {
269 let parent = node.parent_element().unwrap();
270 parent.remove_child(node).unwrap();
271 }
272
273 *self = next;
274 }
275 }
276 }
277
278 fn enter_diff(&mut self) {
279 match self {
280 WebCursor::AttrsOf(_, mode) => {
281 *mode = Mode::Diff;
282 }
283 WebCursor::AfterLastChild(_, mode) => {
284 *mode = Mode::Diff;
285 }
286 WebCursor::Node(_, mode) => {
287 *mode = Mode::Diff;
288 }
289 WebCursor::Detached => {}
290 }
291 }
292
293 fn exit_diff(&mut self) {
294 match self {
295 WebCursor::AttrsOf(_, mode) => {
296 *mode = Mode::Append;
297 }
298 WebCursor::AfterLastChild(_, mode) => {
299 *mode = Mode::Append;
300 }
301 WebCursor::Node(_, mode) => {
302 *mode = Mode::Append;
303 }
304 WebCursor::Detached => {}
305 }
306 }
307
308 fn replace(&mut self, func: impl FnOnce(&mut Self)) {
309 let mut replacement_cursor = WebCursor::Detached;
310 func(&mut replacement_cursor);
311
312 match (&self, replacement_cursor) {
313 (WebCursor::Detached, _) => {}
314 (WebCursor::Node(node, _mode), WebCursor::Node(replacement, mode2)) => {
315 let parent = node.parent_element().unwrap();
316 parent.replace_child(&replacement, node).unwrap();
317
318 *self = WebCursor::Node(replacement, mode2);
319 }
320 (WebCursor::Node(_node, _), WebCursor::Detached) => {
321 panic!();
322 }
323 (WebCursor::AttrsOf(_el, _), _) => {
324 panic!()
325 }
326 (WebCursor::AfterLastChild(..), _) => {
327 panic!();
328 }
329 _ => panic!(),
330 }
331 }
332}
333
334impl WebCursor {
335 fn append_node(&mut self, appendee: &web_sys::Node) {
336 match self {
338 Self::Detached => {
339 *self = Self::Node(appendee.clone(), Mode::Append);
340 }
341 Self::AfterLastChild(element, mode) => {
342 element.append_child(appendee).expect("A");
344 *self = Self::Node(appendee.clone(), *mode);
345 }
346 Self::Node(node, mode) => {
347 node.parent_element()
349 .expect("parent element of node")
350 .insert_before(appendee, node.next_sibling().as_ref())
351 .expect("insert_before");
352 *self = Self::Node(appendee.clone(), *mode);
353 }
354 Self::AttrsOf(..) => panic!("append to attrs"),
355 }
356 }
357
358 fn handle(&self) -> web_sys::Node {
359 match self {
360 Self::Detached => panic!(),
361 Self::Node(node, _) => node.clone(),
362 Self::AfterLastChild(..) => panic!(),
363 Self::AttrsOf(..) => panic!(),
364 }
365 }
366}
367
368fn document() -> Document {
369 window().unwrap().document().unwrap()
370}