olddom.rs 30 KB

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