lib.rs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661
  1. //! The virtual_node module exposes the `VirtualNode` struct and methods that power our
  2. //! virtual dom.
  3. // TODO: A few of these dependencies (including js_sys) are used to power events.. yet events
  4. // only work on wasm32 targest. So we should start sprinkling some
  5. //
  6. // #[cfg(target_arch = "wasm32")]
  7. // #[cfg(not(target_arch = "wasm32"))]
  8. //
  9. // Around in order to get rid of dependencies that we don't need in non wasm32 targets
  10. use std::collections::HashMap;
  11. use std::fmt;
  12. use std::rc::Rc;
  13. pub mod virtual_node_test_utils;
  14. use web_sys::{self, Element, EventTarget, Node, Text};
  15. use wasm_bindgen::JsCast;
  16. use wasm_bindgen::JsValue;
  17. use std::ops::Deref;
  18. use std::sync::Mutex;
  19. // Used to uniquely identify elements that contain closures so that the DomUpdater can
  20. // look them up by their unique id.
  21. // When the DomUpdater sees that the element no longer exists it will drop all of it's
  22. // Rc'd Closures for those events.
  23. use lazy_static::lazy_static;
  24. lazy_static! {
  25. static ref ELEM_UNIQUE_ID: Mutex<u32> = Mutex::new(0);
  26. }
  27. /// When building your views you'll typically use the `html!` macro to generate
  28. /// `VirtualNode`'s.
  29. ///
  30. /// `html! { <div> <span></span> </div> }` really generates a `VirtualNode` with
  31. /// one child (span).
  32. ///
  33. /// Later, on the client side, you'll use the `diff` and `patch` modules to
  34. /// update the real DOM with your latest tree of virtual nodes (virtual dom).
  35. ///
  36. /// Or on the server side you'll just call `.to_string()` on your root virtual node
  37. /// in order to recursively render the node and all of its children.
  38. ///
  39. /// TODO: Make all of these fields private and create accessor methods
  40. /// TODO: Create a builder to create instances of VirtualNode::Element with
  41. /// attrs and children without having to explicitly create a VElement
  42. #[derive(PartialEq)]
  43. pub enum VirtualNode {
  44. /// An element node (node type `ELEMENT_NODE`).
  45. Element(VElement),
  46. /// A text node (node type `TEXT_NODE`).
  47. ///
  48. /// Note: This wraps a `VText` instead of a plain `String` in
  49. /// order to enable custom methods like `create_text_node()` on the
  50. /// wrapped type.
  51. Text(VText),
  52. /// A User-defined componen node (node type COMPONENT_NODE)
  53. Component(VComponent),
  54. }
  55. #[derive(PartialEq)]
  56. pub struct VElement {
  57. /// The HTML tag, such as "div"
  58. pub tag: String,
  59. /// HTML attributes such as id, class, style, etc
  60. pub attrs: HashMap<String, String>,
  61. /// Events that will get added to your real DOM element via `.addEventListener`
  62. pub events: Events,
  63. /// The children of this `VirtualNode`. So a <div> <em></em> </div> structure would
  64. /// have a parent div and one child, em.
  65. pub children: Vec<VirtualNode>,
  66. }
  67. #[derive(PartialEq)]
  68. pub struct VText {
  69. pub text: String,
  70. }
  71. #[derive(PartialEq)]
  72. pub struct VComponent {}
  73. impl VirtualNode {
  74. /// Create a new virtual element node with a given tag.
  75. ///
  76. /// These get patched into the DOM using `document.createElement`
  77. ///
  78. /// ```ignore
  79. /// use virtual_dom_rs::VirtualNode;
  80. ///
  81. /// let div = VirtualNode::element("div");
  82. /// ```
  83. pub fn element<S>(tag: S) -> Self
  84. where
  85. S: Into<String>,
  86. {
  87. VirtualNode::Element(VElement::new(tag))
  88. }
  89. /// Create a new virtual text node with the given text.
  90. ///
  91. /// These get patched into the DOM using `document.createTextNode`
  92. ///
  93. /// ```ignore
  94. /// use virtual_dom_rs::VirtualNode;
  95. ///
  96. /// let div = VirtualNode::text("div");
  97. /// ```
  98. pub fn text<S>(text: S) -> Self
  99. where
  100. S: Into<String>,
  101. {
  102. VirtualNode::Text(VText::new(text.into()))
  103. }
  104. /// Return a [`VElement`] reference, if this is an [`Element`] variant.
  105. ///
  106. /// [`VElement`]: struct.VElement.html
  107. /// [`Element`]: enum.VirtualNode.html#variant.Element
  108. pub fn as_velement_ref(&self) -> Option<&VElement> {
  109. match self {
  110. VirtualNode::Element(ref element_node) => Some(element_node),
  111. _ => None,
  112. }
  113. }
  114. /// Return a mutable [`VElement`] reference, if this is an [`Element`] variant.
  115. ///
  116. /// [`VElement`]: struct.VElement.html
  117. /// [`Element`]: enum.VirtualNode.html#variant.Element
  118. pub fn as_velement_mut(&mut self) -> Option<&mut VElement> {
  119. match self {
  120. VirtualNode::Element(ref mut element_node) => Some(element_node),
  121. _ => None,
  122. }
  123. }
  124. /// Return a [`VText`] reference, if this is an [`Text`] variant.
  125. ///
  126. /// [`VText`]: struct.VText.html
  127. /// [`Text`]: enum.VirtualNode.html#variant.Text
  128. pub fn as_vtext_ref(&self) -> Option<&VText> {
  129. match self {
  130. VirtualNode::Text(ref text_node) => Some(text_node),
  131. _ => None,
  132. }
  133. }
  134. /// Return a mutable [`VText`] reference, if this is an [`Text`] variant.
  135. ///
  136. /// [`VText`]: struct.VText.html
  137. /// [`Text`]: enum.VirtualNode.html#variant.Text
  138. pub fn as_vtext_mut(&mut self) -> Option<&mut VText> {
  139. match self {
  140. VirtualNode::Text(ref mut text_node) => Some(text_node),
  141. _ => None,
  142. }
  143. }
  144. /// Create and return a `CreatedNode` instance (containing a DOM `Node`
  145. /// together with potentially related closures) for this virtual node.
  146. pub fn create_dom_node(&self) -> CreatedNode<Node> {
  147. match self {
  148. VirtualNode::Text(text_node) => {
  149. CreatedNode::without_closures(text_node.create_text_node())
  150. }
  151. VirtualNode::Element(element_node) => element_node.create_element_node().into(),
  152. VirtualNode::Component(_) => todo!("WIP on Component Syntax"),
  153. }
  154. }
  155. /// Used by html-macro to insert space before text that is inside of a block that came after
  156. /// an open tag.
  157. ///
  158. /// html! { <div> {world}</div> }
  159. ///
  160. /// So that we end up with <div> world</div> when we're finished parsing.
  161. pub fn insert_space_before_text(&mut self) {
  162. match self {
  163. VirtualNode::Text(text_node) => {
  164. text_node.text = " ".to_string() + &text_node.text;
  165. }
  166. _ => {}
  167. }
  168. }
  169. /// Used by html-macro to insert space after braced text if we know that the next block is
  170. /// another block or a closing tag.
  171. ///
  172. /// html! { <div>{Hello} {world}</div> } -> <div>Hello world</div>
  173. /// html! { <div>{Hello} </div> } -> <div>Hello </div>
  174. ///
  175. /// So that we end up with <div>Hello world</div> when we're finished parsing.
  176. pub fn insert_space_after_text(&mut self) {
  177. match self {
  178. VirtualNode::Text(text_node) => {
  179. text_node.text += " ";
  180. }
  181. _ => {}
  182. }
  183. }
  184. }
  185. impl VElement {
  186. pub fn new<S>(tag: S) -> Self
  187. where
  188. S: Into<String>,
  189. {
  190. VElement {
  191. tag: tag.into(),
  192. attrs: HashMap::new(),
  193. events: Events(HashMap::new()),
  194. children: vec![],
  195. }
  196. }
  197. /// Build a DOM element by recursively creating DOM nodes for this element and it's
  198. /// children, it's children's children, etc.
  199. pub fn create_element_node(&self) -> CreatedNode<Element> {
  200. let document = web_sys::window().unwrap().document().unwrap();
  201. let element = if html_validation::is_svg_namespace(&self.tag) {
  202. document
  203. .create_element_ns(Some("http://www.w3.org/2000/svg"), &self.tag)
  204. .unwrap()
  205. } else {
  206. document.create_element(&self.tag).unwrap()
  207. };
  208. let mut closures = HashMap::new();
  209. self.attrs.iter().for_each(|(name, value)| {
  210. if name == "unsafe_inner_html" {
  211. element.set_inner_html(value);
  212. return;
  213. }
  214. element
  215. .set_attribute(name, value)
  216. .expect("Set element attribute in create element");
  217. });
  218. if self.events.0.len() > 0 {
  219. let unique_id = create_unique_identifier();
  220. element
  221. .set_attribute("data-vdom-id".into(), &unique_id.to_string())
  222. .expect("Could not set attribute on element");
  223. closures.insert(unique_id, vec![]);
  224. self.events.0.iter().for_each(|(onevent, callback)| {
  225. // onclick -> click
  226. let event = &onevent[2..];
  227. let current_elem: &EventTarget = element.dyn_ref().unwrap();
  228. current_elem
  229. .add_event_listener_with_callback(
  230. event,
  231. callback.as_ref().as_ref().unchecked_ref(),
  232. )
  233. .unwrap();
  234. closures
  235. .get_mut(&unique_id)
  236. .unwrap()
  237. .push(Rc::clone(callback));
  238. });
  239. }
  240. let mut previous_node_was_text = false;
  241. self.children.iter().for_each(|child| {
  242. match child {
  243. VirtualNode::Text(text_node) => {
  244. let current_node = element.as_ref() as &web_sys::Node;
  245. // We ensure that the text siblings are patched by preventing the browser from merging
  246. // neighboring text nodes. Originally inspired by some of React's work from 2016.
  247. // -> https://reactjs.org/blog/2016/04/07/react-v15.html#major-changes
  248. // -> https://github.com/facebook/react/pull/5753
  249. //
  250. // `ptns` = Percy text node separator
  251. if previous_node_was_text {
  252. let separator = document.create_comment("ptns");
  253. current_node
  254. .append_child(separator.as_ref() as &web_sys::Node)
  255. .unwrap();
  256. }
  257. current_node
  258. .append_child(&text_node.create_text_node())
  259. .unwrap();
  260. previous_node_was_text = true;
  261. }
  262. VirtualNode::Element(element_node) => {
  263. previous_node_was_text = false;
  264. let child = element_node.create_element_node();
  265. let child_elem: Element = child.node;
  266. closures.extend(child.closures);
  267. element.append_child(&child_elem).unwrap();
  268. }
  269. VirtualNode::Component(_) => {
  270. todo!("WIP on Component Syntax")
  271. }
  272. }
  273. });
  274. if let Some(on_create_elem) = self.events.0.get("on_create_elem") {
  275. let on_create_elem: &js_sys::Function =
  276. on_create_elem.as_ref().as_ref().unchecked_ref();
  277. on_create_elem
  278. .call1(&wasm_bindgen::JsValue::NULL, &element)
  279. .unwrap();
  280. }
  281. CreatedNode {
  282. node: element,
  283. closures,
  284. }
  285. }
  286. }
  287. impl VText {
  288. /// Create an new `VText` instance with the specified text.
  289. pub fn new<S>(text: S) -> Self
  290. where
  291. S: Into<String>,
  292. {
  293. VText { text: text.into() }
  294. }
  295. /// Return a `Text` element from a `VirtualNode`, typically right before adding it
  296. /// into the DOM.
  297. pub fn create_text_node(&self) -> Text {
  298. let document = web_sys::window().unwrap().document().unwrap();
  299. document.create_text_node(&self.text)
  300. }
  301. }
  302. /// A node along with all of the closures that were created for that
  303. /// node's events and all of it's child node's events.
  304. pub struct CreatedNode<T> {
  305. /// A `Node` or `Element` that was created from a `VirtualNode`
  306. pub node: T,
  307. /// A map of a node's unique identifier along with all of the Closures for that node.
  308. ///
  309. /// The DomUpdater uses this to look up nodes and see if they're still in the page. If not
  310. /// the reference that we maintain to their closure will be dropped, thus freeing the Closure's
  311. /// memory.
  312. pub closures: HashMap<u32, Vec<DynClosure>>,
  313. }
  314. impl<T> CreatedNode<T> {
  315. pub fn without_closures<N: Into<T>>(node: N) -> Self {
  316. CreatedNode {
  317. node: node.into(),
  318. closures: HashMap::with_capacity(0),
  319. }
  320. }
  321. }
  322. impl<T> Deref for CreatedNode<T> {
  323. type Target = T;
  324. fn deref(&self) -> &Self::Target {
  325. &self.node
  326. }
  327. }
  328. impl From<CreatedNode<Element>> for CreatedNode<Node> {
  329. fn from(other: CreatedNode<Element>) -> CreatedNode<Node> {
  330. CreatedNode {
  331. node: other.node.into(),
  332. closures: other.closures,
  333. }
  334. }
  335. }
  336. fn create_unique_identifier() -> u32 {
  337. let mut elem_unique_id = ELEM_UNIQUE_ID.lock().unwrap();
  338. *elem_unique_id += 1;
  339. *elem_unique_id
  340. }
  341. /// A trait with common functionality for rendering front-end views.
  342. pub trait View {
  343. /// Render a VirtualNode, or any IntoIter<VirtualNode>
  344. fn render(&self) -> VirtualNode;
  345. }
  346. impl<V> From<&V> for VirtualNode
  347. where
  348. V: View,
  349. {
  350. fn from(v: &V) -> Self {
  351. v.render()
  352. }
  353. }
  354. /// Used by the html! macro for all braced child nodes so that we can use any type
  355. /// that implements Into<IterableNodes>
  356. ///
  357. /// html! { <div> { nodes } </div> }
  358. ///
  359. /// nodes can be a String .. VirtualNode .. Vec<VirtualNode> ... etc
  360. pub struct IterableNodes(Vec<VirtualNode>);
  361. impl IterableNodes {
  362. /// Retrieve the first node mutably
  363. pub fn first(&mut self) -> &mut VirtualNode {
  364. self.0.first_mut().unwrap()
  365. }
  366. /// Retrieve the last node mutably
  367. pub fn last(&mut self) -> &mut VirtualNode {
  368. self.0.last_mut().unwrap()
  369. }
  370. }
  371. impl IntoIterator for IterableNodes {
  372. type Item = VirtualNode;
  373. // TODO: Is this possible with an array [VirtualNode] instead of a vec?
  374. type IntoIter = ::std::vec::IntoIter<VirtualNode>;
  375. fn into_iter(self) -> Self::IntoIter {
  376. self.0.into_iter()
  377. }
  378. }
  379. impl From<VirtualNode> for IterableNodes {
  380. fn from(other: VirtualNode) -> Self {
  381. IterableNodes(vec![other])
  382. }
  383. }
  384. impl From<&str> for IterableNodes {
  385. fn from(other: &str) -> Self {
  386. IterableNodes(vec![VirtualNode::text(other)])
  387. }
  388. }
  389. impl From<String> for IterableNodes {
  390. fn from(other: String) -> Self {
  391. IterableNodes(vec![VirtualNode::text(other.as_str())])
  392. }
  393. }
  394. impl From<Vec<VirtualNode>> for IterableNodes {
  395. fn from(other: Vec<VirtualNode>) -> Self {
  396. IterableNodes(other)
  397. }
  398. }
  399. impl<V: View> From<Vec<V>> for IterableNodes {
  400. fn from(other: Vec<V>) -> Self {
  401. IterableNodes(other.into_iter().map(|it| it.render()).collect())
  402. }
  403. }
  404. impl<V: View> From<&Vec<V>> for IterableNodes {
  405. fn from(other: &Vec<V>) -> Self {
  406. IterableNodes(other.iter().map(|it| it.render()).collect())
  407. }
  408. }
  409. impl<V: View> From<&[V]> for IterableNodes {
  410. fn from(other: &[V]) -> Self {
  411. IterableNodes(other.iter().map(|it| it.render()).collect())
  412. }
  413. }
  414. impl From<VText> for VirtualNode {
  415. fn from(other: VText) -> Self {
  416. VirtualNode::Text(other)
  417. }
  418. }
  419. impl From<VElement> for VirtualNode {
  420. fn from(other: VElement) -> Self {
  421. VirtualNode::Element(other)
  422. }
  423. }
  424. impl From<&str> for VirtualNode {
  425. fn from(other: &str) -> Self {
  426. VirtualNode::text(other)
  427. }
  428. }
  429. impl From<String> for VirtualNode {
  430. fn from(other: String) -> Self {
  431. VirtualNode::text(other.as_str())
  432. }
  433. }
  434. impl From<&str> for VText {
  435. fn from(text: &str) -> Self {
  436. VText {
  437. text: text.to_string(),
  438. }
  439. }
  440. }
  441. impl From<String> for VText {
  442. fn from(text: String) -> Self {
  443. VText { text }
  444. }
  445. }
  446. impl IntoIterator for VirtualNode {
  447. type Item = VirtualNode;
  448. // TODO: Is this possible with an array [VirtualNode] instead of a vec?
  449. type IntoIter = ::std::vec::IntoIter<VirtualNode>;
  450. fn into_iter(self) -> Self::IntoIter {
  451. vec![self].into_iter()
  452. }
  453. }
  454. impl Into<::std::vec::IntoIter<VirtualNode>> for VirtualNode {
  455. fn into(self) -> ::std::vec::IntoIter<VirtualNode> {
  456. self.into_iter()
  457. }
  458. }
  459. impl fmt::Debug for VirtualNode {
  460. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  461. match self {
  462. VirtualNode::Element(e) => write!(f, "Node::{:?}", e),
  463. VirtualNode::Text(t) => write!(f, "Node::{:?}", t),
  464. VirtualNode::Component(c) => write!(f, "Node::{:?}", c),
  465. }
  466. }
  467. }
  468. impl fmt::Debug for VElement {
  469. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  470. write!(
  471. f,
  472. "Element(<{}>, attrs: {:?}, children: {:?})",
  473. self.tag, self.attrs, self.children,
  474. )
  475. }
  476. }
  477. impl fmt::Debug for VText {
  478. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  479. write!(f, "Text({})", self.text)
  480. }
  481. }
  482. impl fmt::Display for VElement {
  483. // Turn a VElement and all of it's children (recursively) into an HTML string
  484. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  485. write!(f, "<{}", self.tag).unwrap();
  486. for (attr, value) in self.attrs.iter() {
  487. write!(f, r#" {}="{}""#, attr, value)?;
  488. }
  489. write!(f, ">")?;
  490. for child in self.children.iter() {
  491. write!(f, "{}", child.to_string())?;
  492. }
  493. if !html_validation::is_self_closing(&self.tag) {
  494. write!(f, "</{}>", self.tag)?;
  495. }
  496. Ok(())
  497. }
  498. }
  499. // Turn a VText into an HTML string
  500. impl fmt::Display for VText {
  501. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  502. write!(f, "{}", self.text)
  503. }
  504. }
  505. // Turn a VirtualNode into an HTML string (delegate impl to variants)
  506. impl fmt::Display for VirtualNode {
  507. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  508. match self {
  509. VirtualNode::Element(element) => write!(f, "{}", element),
  510. VirtualNode::Text(text) => write!(f, "{}", text),
  511. }
  512. }
  513. }
  514. /// Box<dyn AsRef<JsValue>>> is our js_sys::Closure. Stored this way to allow us to store
  515. /// any Closure regardless of the arguments.
  516. pub type DynClosure = Rc<dyn AsRef<JsValue>>;
  517. /// We need a custom implementation of fmt::Debug since JsValue doesn't
  518. /// implement debug.
  519. pub struct Events(pub HashMap<String, DynClosure>);
  520. impl PartialEq for Events {
  521. // TODO: What should happen here..? And why?
  522. fn eq(&self, _rhs: &Self) -> bool {
  523. true
  524. }
  525. }
  526. impl fmt::Debug for Events {
  527. // Print out all of the event names for this VirtualNode
  528. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  529. let events: String = self.0.keys().map(|key| " ".to_string() + key).collect();
  530. write!(f, "{}", events)
  531. }
  532. }
  533. #[cfg(test)]
  534. mod tests {
  535. use super::*;
  536. #[test]
  537. fn self_closing_tag_to_string() {
  538. let node = VirtualNode::element("br");
  539. // No </br> since self closing tag
  540. assert_eq!(&node.to_string(), "<br>");
  541. }
  542. #[test]
  543. fn to_string() {
  544. let mut node = VirtualNode::Element(VElement::new("div"));
  545. node.as_velement_mut()
  546. .unwrap()
  547. .attrs
  548. .insert("id".into(), "some-id".into());
  549. let mut child = VirtualNode::Element(VElement::new("span"));
  550. let mut text = VirtualNode::Text(VText::new("Hello world"));
  551. child.as_velement_mut().unwrap().children.push(text);
  552. node.as_velement_mut().unwrap().children.push(child);
  553. let expected = r#"<div id="some-id"><span>Hello world</span></div>"#;
  554. assert_eq!(node.to_string(), expected);
  555. }
  556. }