dom.rs 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805
  1. //! Implementation of a renderer for Dioxus on the web.
  2. //!
  3. //! Oustanding todos:
  4. //! - Removing event listeners (delegation)
  5. //! - Passive event listeners
  6. //! - no-op event listener patch for safari
  7. //! - tests to ensure dyn_into works for various event types.
  8. //! - Partial delegation?>
  9. use dioxus_core::{DomEdit, ElementId, SchedulerMsg, ScopeId, UserEvent};
  10. use fxhash::FxHashMap;
  11. use std::{any::Any, fmt::Debug, rc::Rc, sync::Arc};
  12. use wasm_bindgen::{closure::Closure, JsCast};
  13. use web_sys::{
  14. CssStyleDeclaration, Document, Element, Event, HtmlElement, HtmlInputElement,
  15. HtmlOptionElement, HtmlTextAreaElement, Node, NodeList,
  16. };
  17. use crate::{nodeslab::NodeSlab, WebConfig};
  18. pub struct WebsysDom {
  19. stack: Stack,
  20. /// A map from ElementID (index) to Node
  21. nodes: NodeSlab,
  22. document: Document,
  23. root: Element,
  24. sender_callback: Rc<dyn Fn(SchedulerMsg)>,
  25. // map of listener types to number of those listeners
  26. // This is roughly a delegater
  27. // TODO: check how infero delegates its events - some are more performant
  28. listeners: FxHashMap<&'static str, ListenerEntry>,
  29. // We need to make sure to add comments between text nodes
  30. // We ensure that the text siblings are patched by preventing the browser from merging
  31. // neighboring text nodes. Originally inspired by some of React's work from 2016.
  32. // -> https://reactjs.org/blog/2016/04/07/react-v15.html#major-changes
  33. // -> https://github.com/facebook/react/pull/5753
  34. last_node_was_text: bool,
  35. }
  36. type ListenerEntry = (usize, Closure<dyn FnMut(&Event)>);
  37. impl WebsysDom {
  38. pub fn new(root: Element, cfg: WebConfig, sender_callback: Rc<dyn Fn(SchedulerMsg)>) -> Self {
  39. let document = load_document();
  40. let mut nodes = NodeSlab::new(2000);
  41. let listeners = FxHashMap::default();
  42. // re-hydrate the page - only supports one virtualdom per page
  43. if cfg.hydrate {
  44. // Load all the elements into the arena
  45. let node_list: NodeList = document.query_selector_all("dio_el").unwrap();
  46. let len = node_list.length() as usize;
  47. for x in 0..len {
  48. let node: Node = node_list.get(x as u32).unwrap();
  49. let el: &Element = node.dyn_ref::<Element>().unwrap();
  50. let id: String = el.get_attribute("dio_el").unwrap();
  51. let id = id.parse::<usize>().unwrap();
  52. nodes[id] = Some(node);
  53. }
  54. // Load all the event listeners into our listener register
  55. // TODO
  56. }
  57. let mut stack = Stack::with_capacity(10);
  58. let root_node = root.clone().dyn_into::<Node>().unwrap();
  59. stack.push(root_node);
  60. Self {
  61. stack,
  62. nodes,
  63. listeners,
  64. document,
  65. sender_callback,
  66. root,
  67. last_node_was_text: false,
  68. }
  69. }
  70. // pub fn apply_refs(&mut self, refs: &[NodeRefMutation]) {
  71. // for item in refs {
  72. // if let Some(bla) = &item.element {
  73. // let node = self.nodes[item.element_id.as_u64() as usize]
  74. // .as_ref()
  75. // .unwrap()
  76. // .clone();
  77. // bla.set(Arc::new(node)).unwrap();
  78. // }
  79. // }
  80. // }
  81. pub fn process_edits(&mut self, edits: &mut Vec<DomEdit>) {
  82. for edit in edits.drain(..) {
  83. match edit {
  84. DomEdit::PushRoot { root } => self.push(root),
  85. DomEdit::PopRoot => self.pop(),
  86. DomEdit::AppendChildren { many } => self.append_children(many),
  87. DomEdit::ReplaceWith { m, root } => self.replace_with(m, root),
  88. DomEdit::Remove { root } => self.remove(root),
  89. DomEdit::CreateTextNode { text, root: id } => self.create_text_node(text, id),
  90. DomEdit::CreateElement { tag, root: id } => self.create_element(tag, None, id),
  91. DomEdit::CreateElementNs { tag, root: id, ns } => {
  92. self.create_element(tag, Some(ns), id)
  93. }
  94. DomEdit::CreatePlaceholder { root: id } => self.create_placeholder(id),
  95. DomEdit::NewEventListener {
  96. event_name,
  97. scope,
  98. root: mounted_node_id,
  99. } => self.new_event_listener(event_name, scope, mounted_node_id),
  100. DomEdit::RemoveEventListener { event, root } => {
  101. self.remove_event_listener(event, root)
  102. }
  103. DomEdit::SetText { text, root } => self.set_text(text, root),
  104. DomEdit::SetAttribute {
  105. field,
  106. value,
  107. ns,
  108. root,
  109. } => self.set_attribute(field, value, ns, root),
  110. DomEdit::RemoveAttribute { name, root } => self.remove_attribute(name, root),
  111. DomEdit::InsertAfter { n, root } => self.insert_after(n, root),
  112. DomEdit::InsertBefore { n, root } => self.insert_before(n, root),
  113. }
  114. }
  115. }
  116. fn push(&mut self, root: u64) {
  117. let key = root as usize;
  118. let domnode = &self.nodes[key];
  119. let real_node: Node = match domnode {
  120. Some(n) => n.clone(),
  121. None => todo!(),
  122. };
  123. self.stack.push(real_node);
  124. }
  125. // drop the node off the stack
  126. fn pop(&mut self) {
  127. self.stack.pop();
  128. }
  129. fn append_children(&mut self, many: u32) {
  130. let root: Node = self
  131. .stack
  132. .list
  133. .get(self.stack.list.len() - (1 + many as usize))
  134. .unwrap()
  135. .clone();
  136. for child in self
  137. .stack
  138. .list
  139. .drain((self.stack.list.len() - many as usize)..)
  140. {
  141. if child.dyn_ref::<web_sys::Text>().is_some() {
  142. if self.last_node_was_text {
  143. let comment_node = self
  144. .document
  145. .create_comment("dioxus")
  146. .dyn_into::<Node>()
  147. .unwrap();
  148. root.append_child(&comment_node).unwrap();
  149. }
  150. self.last_node_was_text = true;
  151. } else {
  152. self.last_node_was_text = false;
  153. }
  154. root.append_child(&child).unwrap();
  155. }
  156. }
  157. fn replace_with(&mut self, m: u32, root: u64) {
  158. let old = self.nodes[root as usize].as_ref().unwrap();
  159. let arr: js_sys::Array = self
  160. .stack
  161. .list
  162. .drain((self.stack.list.len() - m as usize)..)
  163. .collect();
  164. if let Some(el) = old.dyn_ref::<Element>() {
  165. el.replace_with_with_node(&arr).unwrap();
  166. } else if let Some(el) = old.dyn_ref::<web_sys::CharacterData>() {
  167. el.replace_with_with_node(&arr).unwrap();
  168. } else if let Some(el) = old.dyn_ref::<web_sys::DocumentType>() {
  169. el.replace_with_with_node(&arr).unwrap();
  170. }
  171. }
  172. fn remove(&mut self, root: u64) {
  173. let node = self.nodes[root as usize].as_ref().unwrap();
  174. if let Some(element) = node.dyn_ref::<Element>() {
  175. element.remove();
  176. } else {
  177. if let Some(parent) = node.parent_node() {
  178. parent.remove_child(&node).unwrap();
  179. }
  180. }
  181. }
  182. fn create_placeholder(&mut self, id: u64) {
  183. self.create_element("pre", None, id);
  184. // self.set_attribute("hidden", "", None);
  185. }
  186. fn create_text_node(&mut self, text: &str, id: u64) {
  187. let textnode = self
  188. .document
  189. .create_text_node(text)
  190. .dyn_into::<Node>()
  191. .unwrap();
  192. self.stack.push(textnode.clone());
  193. self.nodes[(id as usize)] = Some(textnode);
  194. }
  195. fn create_element(&mut self, tag: &str, ns: Option<&'static str>, id: u64) {
  196. let tag = wasm_bindgen::intern(tag);
  197. let el = match ns {
  198. Some(ns) => self
  199. .document
  200. .create_element_ns(Some(ns), tag)
  201. .unwrap()
  202. .dyn_into::<Node>()
  203. .unwrap(),
  204. None => self
  205. .document
  206. .create_element(tag)
  207. .unwrap()
  208. .dyn_into::<Node>()
  209. .unwrap(),
  210. };
  211. let el2 = el.dyn_ref::<Element>().unwrap();
  212. el2.set_attribute("dioxus-id", &format!("{}", id)).unwrap();
  213. self.stack.push(el.clone());
  214. self.nodes[(id as usize)] = Some(el);
  215. }
  216. fn new_event_listener(&mut self, event: &'static str, scope: ScopeId, real_id: u64) {
  217. let event = wasm_bindgen::intern(event);
  218. // attach the correct attributes to the element
  219. // these will be used by accessing the event's target
  220. // This ensures we only ever have one handler attached to the root, but decide
  221. // dynamically when we want to call a listener.
  222. let el = self.stack.top();
  223. let el = el
  224. .dyn_ref::<Element>()
  225. .expect(&format!("not an element: {:?}", el));
  226. // let scope_id = scope.data().as_ffi();
  227. let scope_id = scope.0 as u64;
  228. el.set_attribute(
  229. &format!("dioxus-event-{}", event),
  230. &format!("{}.{}", scope_id, real_id),
  231. )
  232. .unwrap();
  233. // el.set_attribute(&format!("dioxus-event"), &format!("{}", event))
  234. // .unwrap();
  235. // Register the callback to decode
  236. if let Some(entry) = self.listeners.get_mut(event) {
  237. entry.0 += 1;
  238. } else {
  239. let trigger = self.sender_callback.clone();
  240. let c: Box<dyn FnMut(&Event)> = Box::new(move |event: &web_sys::Event| {
  241. // "Result" cannot be received from JS
  242. // Instead, we just build and immediately execute a closure that returns result
  243. match decode_trigger(event) {
  244. Ok(synthetic_event) => trigger.as_ref()(SchedulerMsg::UiEvent(synthetic_event)),
  245. Err(e) => log::error!("Error decoding Dioxus event attribute. {:#?}", e),
  246. };
  247. });
  248. let handler = Closure::wrap(c);
  249. self.root
  250. .add_event_listener_with_callback(event, (&handler).as_ref().unchecked_ref())
  251. .unwrap();
  252. // Increment the listeners
  253. self.listeners.insert(event.into(), (1, handler));
  254. }
  255. }
  256. fn remove_event_listener(&mut self, _event: &str, _root: u64) {
  257. todo!()
  258. }
  259. fn set_text(&mut self, text: &str, root: u64) {
  260. let el = self.nodes[root as usize].as_ref().unwrap();
  261. el.set_text_content(Some(text))
  262. }
  263. fn set_attribute(&mut self, name: &str, value: &str, ns: Option<&str>, root: u64) {
  264. let node = self.nodes[root as usize].as_ref().unwrap();
  265. if ns == Some("style") {
  266. if let Some(el) = node.dyn_ref::<Element>() {
  267. let el = el.dyn_ref::<HtmlElement>().unwrap();
  268. let style_dc: CssStyleDeclaration = el.style();
  269. style_dc.set_property(name, value).unwrap();
  270. }
  271. } else {
  272. let fallback = || {
  273. let el = node.dyn_ref::<Element>().unwrap();
  274. el.set_attribute(name, value).unwrap()
  275. };
  276. match name {
  277. "dangerous_inner_html" => {
  278. if let Some(el) = node.dyn_ref::<Element>() {
  279. el.set_inner_html(value);
  280. }
  281. }
  282. "value" => {
  283. if let Some(input) = node.dyn_ref::<HtmlInputElement>() {
  284. /*
  285. if the attribute being set is the same as the value of the input, then don't bother setting it.
  286. This is used in controlled components to keep the cursor in the right spot.
  287. this logic should be moved into the virtualdom since we have the notion of "volatile"
  288. */
  289. if input.value() != value {
  290. input.set_value(value);
  291. }
  292. } else if let Some(node) = node.dyn_ref::<HtmlTextAreaElement>() {
  293. if name == "value" {
  294. node.set_value(value);
  295. }
  296. } else {
  297. fallback();
  298. }
  299. }
  300. "checked" => {
  301. if let Some(input) = node.dyn_ref::<HtmlInputElement>() {
  302. match value {
  303. "true" => input.set_checked(true),
  304. "false" => input.set_checked(false),
  305. _ => fallback(),
  306. }
  307. } else {
  308. fallback();
  309. }
  310. }
  311. "selected" => {
  312. if let Some(node) = node.dyn_ref::<HtmlOptionElement>() {
  313. node.set_selected(true);
  314. } else {
  315. fallback();
  316. }
  317. }
  318. _ => fallback(),
  319. }
  320. }
  321. }
  322. fn remove_attribute(&mut self, name: &str, root: u64) {
  323. let node = self.nodes[root as usize].as_ref().unwrap();
  324. if let Some(node) = node.dyn_ref::<web_sys::Element>() {
  325. node.remove_attribute(name).unwrap();
  326. }
  327. if let Some(node) = node.dyn_ref::<HtmlInputElement>() {
  328. // Some attributes are "volatile" and don't work through `removeAttribute`.
  329. if name == "value" {
  330. node.set_value("");
  331. }
  332. if name == "checked" {
  333. node.set_checked(false);
  334. }
  335. }
  336. if let Some(node) = node.dyn_ref::<HtmlOptionElement>() {
  337. if name == "selected" {
  338. node.set_selected(true);
  339. }
  340. }
  341. }
  342. fn insert_after(&mut self, n: u32, root: u64) {
  343. let old = self.nodes[root as usize].as_ref().unwrap();
  344. let arr: js_sys::Array = self
  345. .stack
  346. .list
  347. .drain((self.stack.list.len() - n as usize)..)
  348. .collect();
  349. if let Some(el) = old.dyn_ref::<Element>() {
  350. el.after_with_node(&arr).unwrap();
  351. } else if let Some(el) = old.dyn_ref::<web_sys::CharacterData>() {
  352. el.after_with_node(&arr).unwrap();
  353. } else if let Some(el) = old.dyn_ref::<web_sys::DocumentType>() {
  354. el.after_with_node(&arr).unwrap();
  355. }
  356. }
  357. fn insert_before(&mut self, n: u32, root: u64) {
  358. let anchor = self.nodes[root as usize].as_ref().unwrap();
  359. if n == 1 {
  360. let before = self.stack.pop();
  361. anchor
  362. .parent_node()
  363. .unwrap()
  364. .insert_before(&before, Some(&anchor))
  365. .unwrap();
  366. } else {
  367. let arr: js_sys::Array = self
  368. .stack
  369. .list
  370. .drain((self.stack.list.len() - n as usize)..)
  371. .collect();
  372. if let Some(el) = anchor.dyn_ref::<Element>() {
  373. el.before_with_node(&arr).unwrap();
  374. } else if let Some(el) = anchor.dyn_ref::<web_sys::CharacterData>() {
  375. el.before_with_node(&arr).unwrap();
  376. } else if let Some(el) = anchor.dyn_ref::<web_sys::DocumentType>() {
  377. el.before_with_node(&arr).unwrap();
  378. }
  379. }
  380. }
  381. }
  382. #[derive(Debug, Default)]
  383. struct Stack {
  384. list: Vec<Node>,
  385. }
  386. impl Stack {
  387. #[inline]
  388. fn with_capacity(cap: usize) -> Self {
  389. Stack {
  390. list: Vec::with_capacity(cap),
  391. }
  392. }
  393. #[inline]
  394. fn push(&mut self, node: Node) {
  395. self.list.push(node);
  396. }
  397. #[inline]
  398. fn pop(&mut self) -> Node {
  399. self.list.pop().unwrap()
  400. }
  401. fn top(&self) -> &Node {
  402. match self.list.last() {
  403. Some(a) => a,
  404. None => panic!("Called 'top' of an empty stack, make sure to push the root first"),
  405. }
  406. }
  407. }
  408. pub struct DioxusWebsysEvent(web_sys::Event);
  409. unsafe impl Send for DioxusWebsysEvent {}
  410. unsafe impl Sync for DioxusWebsysEvent {}
  411. // trait MyTrait {}
  412. // impl MyTrait for web_sys::Event {}
  413. // todo: some of these events are being casted to the wrong event type.
  414. // We need tests that simulate clicks/etc and make sure every event type works.
  415. fn virtual_event_from_websys_event(event: web_sys::Event) -> Arc<dyn Any + Send + Sync> {
  416. use dioxus_html::on::*;
  417. use dioxus_html::KeyCode;
  418. // use dioxus_core::events::on::*;
  419. match event.type_().as_str() {
  420. "copy" | "cut" | "paste" => Arc::new(ClipboardEvent {}),
  421. "compositionend" | "compositionstart" | "compositionupdate" => {
  422. let evt: &web_sys::CompositionEvent = event.dyn_ref().unwrap();
  423. Arc::new(CompositionEvent {
  424. data: evt.data().unwrap_or_default(),
  425. })
  426. }
  427. "keydown" | "keypress" | "keyup" => {
  428. let evt: &web_sys::KeyboardEvent = event.dyn_ref().unwrap();
  429. Arc::new(KeyboardEvent {
  430. alt_key: evt.alt_key(),
  431. char_code: evt.char_code(),
  432. key: evt.key(),
  433. key_code: KeyCode::from_raw_code(evt.key_code() as u8),
  434. ctrl_key: evt.ctrl_key(),
  435. locale: "not implemented".to_string(),
  436. location: evt.location() as usize,
  437. meta_key: evt.meta_key(),
  438. repeat: evt.repeat(),
  439. shift_key: evt.shift_key(),
  440. which: evt.which() as usize,
  441. })
  442. }
  443. "focus" | "blur" => {
  444. //
  445. Arc::new(FocusEvent {})
  446. }
  447. // "change" => SyntheticEvent::GenericEvent(DioxusEvent::new((), DioxusWebsysEvent(event))),
  448. // todo: these handlers might get really slow if the input box gets large and allocation pressure is heavy
  449. // don't have a good solution with the serialized event problem
  450. "change" | "input" | "invalid" | "reset" | "submit" => {
  451. let evt: &web_sys::Event = event.dyn_ref().unwrap();
  452. let target: web_sys::EventTarget = evt.target().unwrap();
  453. let value: String = (&target)
  454. .dyn_ref()
  455. .map(|input: &web_sys::HtmlInputElement| {
  456. // todo: special case more input types
  457. match input.type_().as_str() {
  458. "checkbox" => {
  459. match input.checked() {
  460. true => "true".to_string(),
  461. false => "false".to_string(),
  462. }
  463. },
  464. _ => {
  465. input.value()
  466. }
  467. }
  468. })
  469. .or_else(|| {
  470. target
  471. .dyn_ref()
  472. .map(|input: &web_sys::HtmlTextAreaElement| input.value())
  473. })
  474. // select elements are NOT input events - because - why woudn't they be??
  475. .or_else(|| {
  476. target
  477. .dyn_ref()
  478. .map(|input: &web_sys::HtmlSelectElement| input.value())
  479. })
  480. .or_else(|| {
  481. target
  482. .dyn_ref::<web_sys::HtmlElement>()
  483. .unwrap()
  484. .text_content()
  485. })
  486. .expect("only an InputElement or TextAreaElement or an element with contenteditable=true can have an oninput event listener");
  487. Arc::new(FormEvent { value })
  488. }
  489. "click" | "contextmenu" | "doubleclick" | "drag" | "dragend" | "dragenter" | "dragexit"
  490. | "dragleave" | "dragover" | "dragstart" | "drop" | "mousedown" | "mouseenter"
  491. | "mouseleave" | "mousemove" | "mouseout" | "mouseover" | "mouseup" => {
  492. let evt: &web_sys::MouseEvent = event.dyn_ref().unwrap();
  493. Arc::new(MouseEvent {
  494. alt_key: evt.alt_key(),
  495. button: evt.button(),
  496. buttons: evt.buttons(),
  497. client_x: evt.client_x(),
  498. client_y: evt.client_y(),
  499. ctrl_key: evt.ctrl_key(),
  500. meta_key: evt.meta_key(),
  501. screen_x: evt.screen_x(),
  502. screen_y: evt.screen_y(),
  503. shift_key: evt.shift_key(),
  504. page_x: evt.page_x(),
  505. page_y: evt.page_y(),
  506. })
  507. }
  508. "pointerdown" | "pointermove" | "pointerup" | "pointercancel" | "gotpointercapture"
  509. | "lostpointercapture" | "pointerenter" | "pointerleave" | "pointerover" | "pointerout" => {
  510. let evt: &web_sys::PointerEvent = event.dyn_ref().unwrap();
  511. Arc::new(PointerEvent {
  512. alt_key: evt.alt_key(),
  513. button: evt.button(),
  514. buttons: evt.buttons(),
  515. client_x: evt.client_x(),
  516. client_y: evt.client_y(),
  517. ctrl_key: evt.ctrl_key(),
  518. meta_key: evt.meta_key(),
  519. page_x: evt.page_x(),
  520. page_y: evt.page_y(),
  521. screen_x: evt.screen_x(),
  522. screen_y: evt.screen_y(),
  523. shift_key: evt.shift_key(),
  524. pointer_id: evt.pointer_id(),
  525. width: evt.width(),
  526. height: evt.height(),
  527. pressure: evt.pressure(),
  528. tangential_pressure: evt.tangential_pressure(),
  529. tilt_x: evt.tilt_x(),
  530. tilt_y: evt.tilt_y(),
  531. twist: evt.twist(),
  532. pointer_type: evt.pointer_type(),
  533. is_primary: evt.is_primary(),
  534. // get_modifier_state: evt.get_modifier_state(),
  535. })
  536. }
  537. "select" => Arc::new(SelectionEvent {}),
  538. "touchcancel" | "touchend" | "touchmove" | "touchstart" => {
  539. let evt: &web_sys::TouchEvent = event.dyn_ref().unwrap();
  540. Arc::new(TouchEvent {
  541. alt_key: evt.alt_key(),
  542. ctrl_key: evt.ctrl_key(),
  543. meta_key: evt.meta_key(),
  544. shift_key: evt.shift_key(),
  545. })
  546. }
  547. "scroll" => Arc::new(()),
  548. "wheel" => {
  549. let evt: &web_sys::WheelEvent = event.dyn_ref().unwrap();
  550. Arc::new(WheelEvent {
  551. delta_x: evt.delta_x(),
  552. delta_y: evt.delta_y(),
  553. delta_z: evt.delta_z(),
  554. delta_mode: evt.delta_mode(),
  555. })
  556. }
  557. "animationstart" | "animationend" | "animationiteration" => {
  558. let evt: &web_sys::AnimationEvent = event.dyn_ref().unwrap();
  559. Arc::new(AnimationEvent {
  560. elapsed_time: evt.elapsed_time(),
  561. animation_name: evt.animation_name(),
  562. pseudo_element: evt.pseudo_element(),
  563. })
  564. }
  565. "transitionend" => {
  566. let evt: &web_sys::TransitionEvent = event.dyn_ref().unwrap();
  567. Arc::new(TransitionEvent {
  568. elapsed_time: evt.elapsed_time(),
  569. property_name: evt.property_name(),
  570. pseudo_element: evt.pseudo_element(),
  571. })
  572. }
  573. "abort" | "canplay" | "canplaythrough" | "durationchange" | "emptied" | "encrypted"
  574. | "ended" | "error" | "loadeddata" | "loadedmetadata" | "loadstart" | "pause" | "play"
  575. | "playing" | "progress" | "ratechange" | "seeked" | "seeking" | "stalled" | "suspend"
  576. | "timeupdate" | "volumechange" | "waiting" => {
  577. //
  578. Arc::new(MediaEvent {})
  579. }
  580. "toggle" => {
  581. //
  582. Arc::new(ToggleEvent {})
  583. }
  584. _ => Arc::new(()),
  585. }
  586. }
  587. /// This function decodes a websys event and produces an EventTrigger
  588. /// With the websys implementation, we attach a unique key to the nodes
  589. fn decode_trigger(event: &web_sys::Event) -> anyhow::Result<UserEvent> {
  590. let target = event
  591. .target()
  592. .expect("missing target")
  593. .dyn_into::<Element>()
  594. .expect("not a valid element");
  595. let typ = event.type_();
  596. use anyhow::Context;
  597. // The error handling here is not very descriptive and needs to be replaced with a zero-cost error system
  598. let val: String = target
  599. .get_attribute(&format!("dioxus-event-{}", typ))
  600. .context(format!("wrong format - received {:#?}", typ))?;
  601. let mut fields = val.splitn(3, ".");
  602. let gi_id = fields
  603. .next()
  604. .and_then(|f| f.parse::<u64>().ok())
  605. .context("failed to parse gi id")?;
  606. let real_id = fields
  607. .next()
  608. .and_then(|raw_id| raw_id.parse::<u64>().ok())
  609. .context("failed to parse real id")?;
  610. let triggered_scope = gi_id;
  611. Ok(UserEvent {
  612. name: event_name_from_typ(&typ),
  613. event: virtual_event_from_websys_event(event.clone()),
  614. mounted_dom_id: Some(ElementId(real_id as usize)),
  615. scope_id: ScopeId(triggered_scope as usize),
  616. priority: dioxus_core::EventPriority::Medium,
  617. })
  618. }
  619. pub(crate) fn load_document() -> Document {
  620. web_sys::window()
  621. .expect("should have access to the Window")
  622. .document()
  623. .expect("should have access to the Document")
  624. }
  625. fn event_name_from_typ(typ: &str) -> &'static str {
  626. match typ {
  627. "copy" => "copy",
  628. "cut" => "cut",
  629. "paste" => "paste",
  630. "compositionend" => "compositionend",
  631. "compositionstart" => "compositionstart",
  632. "compositionupdate" => "compositionupdate",
  633. "keydown" => "keydown",
  634. "keypress" => "keypress",
  635. "keyup" => "keyup",
  636. "focus" => "focus",
  637. "blur" => "blur",
  638. "change" => "change",
  639. "input" => "input",
  640. "invalid" => "invalid",
  641. "reset" => "reset",
  642. "submit" => "submit",
  643. "click" => "click",
  644. "contextmenu" => "contextmenu",
  645. "doubleclick" => "doubleclick",
  646. "drag" => "drag",
  647. "dragend" => "dragend",
  648. "dragenter" => "dragenter",
  649. "dragexit" => "dragexit",
  650. "dragleave" => "dragleave",
  651. "dragover" => "dragover",
  652. "dragstart" => "dragstart",
  653. "drop" => "drop",
  654. "mousedown" => "mousedown",
  655. "mouseenter" => "mouseenter",
  656. "mouseleave" => "mouseleave",
  657. "mousemove" => "mousemove",
  658. "mouseout" => "mouseout",
  659. "mouseover" => "mouseover",
  660. "mouseup" => "mouseup",
  661. "pointerdown" => "pointerdown",
  662. "pointermove" => "pointermove",
  663. "pointerup" => "pointerup",
  664. "pointercancel" => "pointercancel",
  665. "gotpointercapture" => "gotpointercapture",
  666. "lostpointercapture" => "lostpointercapture",
  667. "pointerenter" => "pointerenter",
  668. "pointerleave" => "pointerleave",
  669. "pointerover" => "pointerover",
  670. "pointerout" => "pointerout",
  671. "select" => "select",
  672. "touchcancel" => "touchcancel",
  673. "touchend" => "touchend",
  674. "touchmove" => "touchmove",
  675. "touchstart" => "touchstart",
  676. "scroll" => "scroll",
  677. "wheel" => "wheel",
  678. "animationstart" => "animationstart",
  679. "animationend" => "animationend",
  680. "animationiteration" => "animationiteration",
  681. "transitionend" => "transitionend",
  682. "abort" => "abort",
  683. "canplay" => "canplay",
  684. "canplaythrough" => "canplaythrough",
  685. "durationchange" => "durationchange",
  686. "emptied" => "emptied",
  687. "encrypted" => "encrypted",
  688. "ended" => "ended",
  689. "error" => "error",
  690. "loadeddata" => "loadeddata",
  691. "loadedmetadata" => "loadedmetadata",
  692. "loadstart" => "loadstart",
  693. "pause" => "pause",
  694. "play" => "play",
  695. "playing" => "playing",
  696. "progress" => "progress",
  697. "ratechange" => "ratechange",
  698. "seeked" => "seeked",
  699. "seeking" => "seeking",
  700. "stalled" => "stalled",
  701. "suspend" => "suspend",
  702. "timeupdate" => "timeupdate",
  703. "volumechange" => "volumechange",
  704. "waiting" => "waiting",
  705. "toggle" => "toggle",
  706. _ => {
  707. panic!("unsupported event type")
  708. }
  709. }
  710. }