new.rs 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604
  1. use std::{collections::HashMap, rc::Rc, sync::Arc};
  2. use dioxus_core::{
  3. events::{EventTrigger, VirtualEvent},
  4. prelude::ScopeIdx,
  5. virtual_dom::RealDomNode,
  6. };
  7. use fxhash::FxHashMap;
  8. use nohash_hasher::IntMap;
  9. use wasm_bindgen::{closure::Closure, JsCast};
  10. use web_sys::{
  11. window, Document, Element, Event, HtmlElement, HtmlInputElement, HtmlOptionElement, Node,
  12. };
  13. pub struct WebsysDom {
  14. pub stack: Stack,
  15. nodes: IntMap<u32, Node>,
  16. document: Document,
  17. root: Element,
  18. event_receiver: async_channel::Receiver<EventTrigger>,
  19. trigger: Arc<dyn Fn(EventTrigger)>,
  20. // every callback gets a monotomically increasing callback ID
  21. callback_id: usize,
  22. // map of listener types to number of those listeners
  23. listeners: FxHashMap<String, (usize, Closure<dyn FnMut(&Event)>)>,
  24. // Map of callback_id to component index and listener id
  25. callback_map: FxHashMap<usize, (usize, usize)>,
  26. // We need to make sure to add comments between text nodes
  27. // We ensure that the text siblings are patched by preventing the browser from merging
  28. // neighboring text nodes. Originally inspired by some of React's work from 2016.
  29. // -> https://reactjs.org/blog/2016/04/07/react-v15.html#major-changes
  30. // -> https://github.com/facebook/react/pull/5753
  31. //
  32. // `ptns` = Percy text node separator
  33. // TODO
  34. last_node_was_text: bool,
  35. node_counter: Counter,
  36. }
  37. impl WebsysDom {
  38. pub fn new(root: Element) -> Self {
  39. let document = window()
  40. .expect("must have access to the window")
  41. .document()
  42. .expect("must have access to the Document");
  43. let (sender, mut receiver) = async_channel::unbounded::<EventTrigger>();
  44. let sender_callback = Arc::new(move |ev| {
  45. let mut c = sender.clone();
  46. wasm_bindgen_futures::spawn_local(async move {
  47. c.send(ev).await.unwrap();
  48. });
  49. });
  50. let mut nodes =
  51. HashMap::with_capacity_and_hasher(1000, nohash_hasher::BuildNoHashHasher::default());
  52. nodes.insert(0_u32, root.clone().dyn_into::<Node>().unwrap());
  53. Self {
  54. stack: Stack::with_capacity(10),
  55. nodes,
  56. callback_id: 0,
  57. listeners: FxHashMap::default(),
  58. callback_map: FxHashMap::default(),
  59. document,
  60. event_receiver: receiver,
  61. trigger: sender_callback,
  62. root,
  63. last_node_was_text: false,
  64. node_counter: Counter(0),
  65. }
  66. }
  67. pub async fn wait_for_event(&mut self) -> Option<EventTrigger> {
  68. let v = self.event_receiver.recv().await.unwrap();
  69. Some(v)
  70. }
  71. }
  72. struct Counter(u32);
  73. impl Counter {
  74. fn next(&mut self) -> u32 {
  75. self.0 += 1;
  76. self.0
  77. }
  78. }
  79. impl dioxus_core::diff::RealDom for WebsysDom {
  80. fn push_root(&mut self, root: dioxus_core::virtual_dom::RealDomNode) {
  81. log::debug!("Called `[`push_root] {:?}", root);
  82. let domnode = self.nodes.get(&root.0).expect("Failed to pop know root");
  83. self.stack.push(domnode.clone());
  84. }
  85. fn append_child(&mut self) {
  86. log::debug!("Called [`append_child`]");
  87. let child = self.stack.pop();
  88. if child.dyn_ref::<web_sys::Text>().is_some() {
  89. if self.last_node_was_text {
  90. let comment_node = self
  91. .document
  92. .create_comment("dioxus")
  93. .dyn_into::<Node>()
  94. .unwrap();
  95. self.stack.top().append_child(&comment_node).unwrap();
  96. }
  97. self.last_node_was_text = true;
  98. } else {
  99. self.last_node_was_text = false;
  100. }
  101. self.stack.top().append_child(&child).unwrap();
  102. }
  103. fn replace_with(&mut self) {
  104. log::debug!("Called [`replace_with`]");
  105. let new_node = self.stack.pop();
  106. let old_node = self.stack.pop();
  107. if old_node.has_type::<Element>() {
  108. old_node
  109. .dyn_ref::<Element>()
  110. .unwrap()
  111. .replace_with_with_node_1(&new_node)
  112. .unwrap();
  113. } else if old_node.has_type::<web_sys::CharacterData>() {
  114. old_node
  115. .dyn_ref::<web_sys::CharacterData>()
  116. .unwrap()
  117. .replace_with_with_node_1(&new_node)
  118. .unwrap();
  119. } else if old_node.has_type::<web_sys::DocumentType>() {
  120. old_node
  121. .dyn_ref::<web_sys::DocumentType>()
  122. .unwrap()
  123. .replace_with_with_node_1(&new_node)
  124. .unwrap();
  125. } else {
  126. panic!("Cannot replace node: {:?}", old_node);
  127. }
  128. // // poc to see if this is a valid solution
  129. // if let Some(id) = self.current_known {
  130. // // update mapping
  131. // self.known_roots.insert(id, new_node.clone());
  132. // self.current_known = None;
  133. // }
  134. self.stack.push(new_node);
  135. }
  136. fn remove(&mut self) {
  137. log::debug!("Called [`remove`]");
  138. todo!()
  139. }
  140. fn remove_all_children(&mut self) {
  141. log::debug!("Called [`remove_all_children`]");
  142. todo!()
  143. }
  144. fn create_text_node(&mut self, text: &str) -> dioxus_core::virtual_dom::RealDomNode {
  145. let nid = self.node_counter.next();
  146. let textnode = self
  147. .document
  148. .create_text_node(text)
  149. .dyn_into::<Node>()
  150. .unwrap();
  151. self.stack.push(textnode.clone());
  152. self.nodes.insert(nid, textnode);
  153. log::debug!("Called [`create_text_node`]: {}, {}", text, nid);
  154. RealDomNode::new(nid)
  155. }
  156. fn create_element(&mut self, tag: &str) -> dioxus_core::virtual_dom::RealDomNode {
  157. let el = self
  158. .document
  159. .create_element(tag)
  160. .unwrap()
  161. .dyn_into::<Node>()
  162. .unwrap();
  163. self.stack.push(el.clone());
  164. let nid = self.node_counter.next();
  165. self.nodes.insert(nid, el);
  166. log::debug!("Called [`create_element`]: {}, {:?}", tag, nid);
  167. RealDomNode::new(nid)
  168. }
  169. fn create_element_ns(
  170. &mut self,
  171. tag: &str,
  172. namespace: &str,
  173. ) -> dioxus_core::virtual_dom::RealDomNode {
  174. let el = self
  175. .document
  176. .create_element_ns(Some(namespace), tag)
  177. .unwrap()
  178. .dyn_into::<Node>()
  179. .unwrap();
  180. self.stack.push(el.clone());
  181. let nid = self.node_counter.next();
  182. self.nodes.insert(nid, el);
  183. log::debug!("Called [`create_element_ns`]: {:}", nid);
  184. RealDomNode::new(nid)
  185. }
  186. fn new_event_listener(
  187. &mut self,
  188. event: &str,
  189. scope: dioxus_core::prelude::ScopeIdx,
  190. el_id: usize,
  191. real_id: RealDomNode,
  192. ) {
  193. log::debug!(
  194. "Called [`new_event_listener`]: {}, {:?}, {}, {:?}",
  195. event,
  196. scope,
  197. el_id,
  198. real_id
  199. );
  200. // attach the correct attributes to the element
  201. // these will be used by accessing the event's target
  202. // This ensures we only ever have one handler attached to the root, but decide
  203. // dynamically when we want to call a listener.
  204. let el = self.stack.top();
  205. let el = el
  206. .dyn_ref::<Element>()
  207. .expect(&format!("not an element: {:?}", el));
  208. let (gi_id, gi_gen) = (&scope).into_raw_parts();
  209. el.set_attribute(
  210. &format!("dioxus-event-{}", event),
  211. &format!("{}.{}.{}.{}", gi_id, gi_gen, el_id, real_id.0),
  212. )
  213. .unwrap();
  214. // Register the callback to decode
  215. if let Some(entry) = self.listeners.get_mut(event) {
  216. entry.0 += 1;
  217. } else {
  218. let trigger = self.trigger.clone();
  219. let handler = Closure::wrap(Box::new(move |event: &web_sys::Event| {
  220. // "Result" cannot be received from JS
  221. // Instead, we just build and immediately execute a closure that returns result
  222. let res = || -> anyhow::Result<EventTrigger> {
  223. log::debug!("Handling event!");
  224. let target = event
  225. .target()
  226. .expect("missing target")
  227. .dyn_into::<Element>()
  228. .expect("not a valid element");
  229. let typ = event.type_();
  230. use anyhow::Context;
  231. let val: String = target
  232. .get_attribute(&format!("dioxus-event-{}", typ))
  233. .context("")?;
  234. let mut fields = val.splitn(4, ".");
  235. let gi_id = fields
  236. .next()
  237. .and_then(|f| f.parse::<usize>().ok())
  238. .context("")?;
  239. let gi_gen = fields
  240. .next()
  241. .and_then(|f| f.parse::<u64>().ok())
  242. .context("")?;
  243. let el_id = fields
  244. .next()
  245. .and_then(|f| f.parse::<usize>().ok())
  246. .context("")?;
  247. let real_id = fields
  248. .next()
  249. .and_then(|f| f.parse::<u32>().ok().map(RealDomNode::new))
  250. .context("")?;
  251. // Call the trigger
  252. log::debug!(
  253. "decoded gi_id: {}, gi_gen: {}, li_idx: {}",
  254. gi_id,
  255. gi_gen,
  256. el_id
  257. );
  258. let triggered_scope = ScopeIdx::from_raw_parts(gi_id, gi_gen);
  259. Ok(EventTrigger::new(
  260. virtual_event_from_websys_event(event),
  261. triggered_scope,
  262. real_id,
  263. ))
  264. };
  265. match res() {
  266. Ok(synthetic_event) => trigger.as_ref()(synthetic_event),
  267. Err(_) => log::error!("Error decoding Dioxus event attribute."),
  268. };
  269. }) as Box<dyn FnMut(&Event)>);
  270. self.root
  271. .add_event_listener_with_callback(event, (&handler).as_ref().unchecked_ref())
  272. .unwrap();
  273. // Increment the listeners
  274. self.listeners.insert(event.into(), (1, handler));
  275. }
  276. }
  277. fn remove_event_listener(&mut self, event: &str) {
  278. log::debug!("Called [`remove_event_listener`]: {}", event);
  279. todo!()
  280. }
  281. fn set_text(&mut self, text: &str) {
  282. log::debug!("Called [`set_text`]: {}", text);
  283. self.stack.top().set_text_content(Some(text))
  284. }
  285. fn set_attribute(&mut self, name: &str, value: &str, is_namespaced: bool) {
  286. log::debug!("Called [`set_attribute`]: {}, {}", name, value);
  287. if name == "class" {
  288. if let Some(el) = self.stack.top().dyn_ref::<Element>() {
  289. el.set_class_name(value);
  290. }
  291. } else {
  292. if let Some(el) = self.stack.top().dyn_ref::<Element>() {
  293. el.set_attribute(name, value).unwrap();
  294. }
  295. }
  296. }
  297. fn remove_attribute(&mut self, name: &str) {
  298. log::debug!("Called [`remove_attribute`]: {}", name);
  299. let node = self.stack.top();
  300. if let Some(node) = node.dyn_ref::<web_sys::Element>() {
  301. node.remove_attribute(name).unwrap();
  302. }
  303. if let Some(node) = node.dyn_ref::<HtmlInputElement>() {
  304. // Some attributes are "volatile" and don't work through `removeAttribute`.
  305. if name == "value" {
  306. node.set_value("");
  307. }
  308. if name == "checked" {
  309. node.set_checked(false);
  310. }
  311. }
  312. if let Some(node) = node.dyn_ref::<HtmlOptionElement>() {
  313. if name == "selected" {
  314. node.set_selected(true);
  315. }
  316. }
  317. }
  318. fn raw_node_as_any_mut(&self) -> &mut dyn std::any::Any {
  319. log::debug!("Called [`raw_node_as_any_mut`]");
  320. todo!()
  321. }
  322. }
  323. #[derive(Debug, Default)]
  324. pub struct Stack {
  325. list: Vec<Node>,
  326. }
  327. impl Stack {
  328. pub fn with_capacity(cap: usize) -> Self {
  329. Stack {
  330. list: Vec::with_capacity(cap),
  331. }
  332. }
  333. pub fn push(&mut self, node: Node) {
  334. // debug!("stack-push: {:?}", node);
  335. self.list.push(node);
  336. }
  337. pub fn pop(&mut self) -> Node {
  338. let res = self.list.pop().unwrap();
  339. res
  340. }
  341. pub fn clear(&mut self) {
  342. self.list.clear();
  343. }
  344. pub fn top(&self) -> &Node {
  345. match self.list.last() {
  346. Some(a) => a,
  347. None => panic!("Called 'top' of an empty stack, make sure to push the root first"),
  348. }
  349. }
  350. }
  351. fn virtual_event_from_websys_event(event: &web_sys::Event) -> VirtualEvent {
  352. use dioxus_core::events::on::*;
  353. match event.type_().as_str() {
  354. "copy" | "cut" | "paste" => {
  355. // let evt: web_sys::ClipboardEvent = event.clone().dyn_into().unwrap();
  356. todo!()
  357. }
  358. "compositionend" | "compositionstart" | "compositionupdate" => {
  359. let evt: web_sys::CompositionEvent = event.clone().dyn_into().unwrap();
  360. todo!()
  361. }
  362. "keydown" | "keypress" | "keyup" => {
  363. let evt: web_sys::KeyboardEvent = event.clone().dyn_into().unwrap();
  364. todo!()
  365. }
  366. "focus" | "blur" => {
  367. let evt: web_sys::FocusEvent = event.clone().dyn_into().unwrap();
  368. todo!()
  369. }
  370. "change" => {
  371. let evt: web_sys::Event = event.clone().dyn_into().expect("wrong error typ");
  372. todo!()
  373. // VirtualEvent::FormEvent(FormEvent {value:})
  374. }
  375. "input" | "invalid" | "reset" | "submit" => {
  376. // is a special react events
  377. let evt: web_sys::InputEvent = event.clone().dyn_into().expect("wrong event type");
  378. let this: web_sys::EventTarget = evt.target().unwrap();
  379. let value = (&this)
  380. .dyn_ref()
  381. .map(|input: &web_sys::HtmlInputElement| input.value())
  382. .or_else(|| {
  383. (&this)
  384. .dyn_ref()
  385. .map(|input: &web_sys::HtmlTextAreaElement| input.value())
  386. })
  387. .or_else(|| {
  388. (&this)
  389. .dyn_ref::<web_sys::HtmlElement>()
  390. .unwrap()
  391. .text_content()
  392. })
  393. .expect("only an InputElement or TextAreaElement or an element with contenteditable=true can have an oninput event listener");
  394. // let p2 = evt.data_transfer();
  395. // let value: Option<String> = (&evt).data();
  396. // let value = val;
  397. // let value = value.unwrap_or_default();
  398. // let value = (&evt).data().expect("No data to unwrap");
  399. // todo - this needs to be a "controlled" event
  400. // these events won't carry the right data with them
  401. todo!()
  402. // VirtualEvent::FormEvent(FormEvent { value })
  403. }
  404. "click" | "contextmenu" | "doubleclick" | "drag" | "dragend" | "dragenter" | "dragexit"
  405. | "dragleave" | "dragover" | "dragstart" | "drop" | "mousedown" | "mouseenter"
  406. | "mouseleave" | "mousemove" | "mouseout" | "mouseover" | "mouseup" => {
  407. let evt: web_sys::MouseEvent = event.clone().dyn_into().unwrap();
  408. #[derive(Debug)]
  409. pub struct CustomMouseEvent(web_sys::MouseEvent);
  410. impl dioxus_core::events::on::MouseEvent for CustomMouseEvent {
  411. fn alt_key(&self) -> bool {
  412. self.0.alt_key()
  413. }
  414. fn button(&self) -> i16 {
  415. self.0.button()
  416. }
  417. fn buttons(&self) -> u16 {
  418. self.0.buttons()
  419. }
  420. fn client_x(&self) -> i32 {
  421. self.0.client_x()
  422. }
  423. fn client_y(&self) -> i32 {
  424. self.0.client_y()
  425. }
  426. fn ctrl_key(&self) -> bool {
  427. self.0.ctrl_key()
  428. }
  429. fn meta_key(&self) -> bool {
  430. self.0.meta_key()
  431. }
  432. fn page_x(&self) -> i32 {
  433. self.0.page_x()
  434. }
  435. fn page_y(&self) -> i32 {
  436. self.0.page_y()
  437. }
  438. fn screen_x(&self) -> i32 {
  439. self.0.screen_x()
  440. }
  441. fn screen_y(&self) -> i32 {
  442. self.0.screen_y()
  443. }
  444. fn shift_key(&self) -> bool {
  445. self.0.shift_key()
  446. }
  447. // yikes
  448. // https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values
  449. fn get_modifier_state(&self, key_code: &str) -> bool {
  450. self.0.get_modifier_state(key_code)
  451. }
  452. }
  453. VirtualEvent::MouseEvent(Rc::new(CustomMouseEvent(evt)))
  454. // MouseEvent(Box::new(RawMouseEvent {
  455. // alt_key: evt.alt_key(),
  456. // button: evt.button() as i32,
  457. // buttons: evt.buttons() as i32,
  458. // client_x: evt.client_x(),
  459. // client_y: evt.client_y(),
  460. // ctrl_key: evt.ctrl_key(),
  461. // meta_key: evt.meta_key(),
  462. // page_x: evt.page_x(),
  463. // page_y: evt.page_y(),
  464. // screen_x: evt.screen_x(),
  465. // screen_y: evt.screen_y(),
  466. // shift_key: evt.shift_key(),
  467. // get_modifier_state: GetModifierKey(Box::new(|f| {
  468. // // evt.get_modifier_state(f)
  469. // todo!("This is not yet implemented properly, sorry :(");
  470. // })),
  471. // }))
  472. // todo!()
  473. // VirtualEvent::MouseEvent()
  474. }
  475. "pointerdown" | "pointermove" | "pointerup" | "pointercancel" | "gotpointercapture"
  476. | "lostpointercapture" | "pointerenter" | "pointerleave" | "pointerover" | "pointerout" => {
  477. let evt: web_sys::PointerEvent = event.clone().dyn_into().unwrap();
  478. todo!()
  479. }
  480. "select" => {
  481. // let evt: web_sys::SelectionEvent = event.clone().dyn_into().unwrap();
  482. // not required to construct anything special beyond standard event stuff
  483. todo!()
  484. }
  485. "touchcancel" | "touchend" | "touchmove" | "touchstart" => {
  486. let evt: web_sys::TouchEvent = event.clone().dyn_into().unwrap();
  487. todo!()
  488. }
  489. "scroll" => {
  490. // let evt: web_sys::UIEvent = event.clone().dyn_into().unwrap();
  491. todo!()
  492. }
  493. "wheel" => {
  494. let evt: web_sys::WheelEvent = event.clone().dyn_into().unwrap();
  495. todo!()
  496. }
  497. "abort" | "canplay" | "canplaythrough" | "durationchange" | "emptied" | "encrypted"
  498. | "ended" | "error" | "loadeddata" | "loadedmetadata" | "loadstart" | "pause" | "play"
  499. | "playing" | "progress" | "ratechange" | "seeked" | "seeking" | "stalled" | "suspend"
  500. | "timeupdate" | "volumechange" | "waiting" => {
  501. // not required to construct anything special beyond standard event stuff
  502. // let evt: web_sys::MediaEvent = event.clone().dyn_into().unwrap();
  503. // let evt: web_sys::MediaEvent = event.clone().dyn_into().unwrap();
  504. todo!()
  505. }
  506. "animationstart" | "animationend" | "animationiteration" => {
  507. let evt: web_sys::AnimationEvent = event.clone().dyn_into().unwrap();
  508. todo!()
  509. }
  510. "transitionend" => {
  511. let evt: web_sys::TransitionEvent = event.clone().dyn_into().unwrap();
  512. todo!()
  513. }
  514. "toggle" => {
  515. // not required to construct anything special beyond standard event stuff (target)
  516. // let evt: web_sys::ToggleEvent = event.clone().dyn_into().unwrap();
  517. todo!()
  518. }
  519. _ => VirtualEvent::OtherEvent,
  520. }
  521. }