Feat: rename recoil to atoms

This commit is contained in:
Jonathan Kelley committed 2021-06-16 11:19:37 -04:00
1 parent 47e896038e
commit 36ea39ae30
29 files changed
+398 -249

No files matched your search

File renamed without changes.
@@ -1,5 +1,5 @@
[package]
name = "recoil"
name = "atoms"
version = "0.0.0"
authors = ["Jonathan Kelley <jkelleyrtp@gmail.com>"]
edition = "2018"
@@ -8,15 +8,15 @@ edition = "2018"
[dependencies]
anyhow = "1.0.40"
dioxus-core = { path = "../core" }
dioxus-core = { path="../core" }
thiserror = "1.0.24"
log = "0.4.14"
im-rc = "15.0.0"
futures = "0.3.15"
[dev-dependencies]
uuid = { version = "0.8.2", features = ["v4", "wasm-bindgen"] }
dioxus-web = { path = "../web" }
uuid = { version="0.8.2", features=["v4", "wasm-bindgen"] }
dioxus-web = { path="../web" }
wasm-bindgen-futures = "*"
wasm-logger = "0.2.0"
@@ -1,10 +1,10 @@
# Recoil.rs - Official global state management solution for Dioxus Apps
# Atom.rs - Official global state management solution for Dioxus Apps
Recoil.rs provides a global state management API for Dioxus apps built on the concept of "atomic state." Instead of grouping state together into a single bundle ALA Redux, Recoil provides individual building blocks of state called Atoms. These atoms can be set/get anywhere in the app and combined to craft complex state. Recoil should be easier to learn and more efficient than Redux. Recoil.rs is modeled after the Recoil.JS project.
Atom.rs provides a global state management API for Dioxus apps built on the concept of "atomic state." Instead of grouping state together into a single bundle ALA Redux, Atom provides individual building blocks of state called Atoms. These atoms can be set/get anywhere in the app and combined to craft complex state. Atom should be easier to learn and more efficient than Redux. Atom.rs is modeled after the Atom.JS project.
Recoil.rs is officially supported by the Dioxus team. By doing so, are are "planting our flag in the stand" for atomic state management instead of bundled (Redux-style) state management. Atomic state management fits well with the internals of Dioxus, meaning Recoil.rs state management will be faster, more efficient, and less sensitive to data races than Redux-style apps.
Atom.rs is officially supported by the Dioxus team. By doing so, we are "planting our flag in the sand" for atomic state management instead of bundled (Redux-style) state management. Atomic state management fits well with the internals of Dioxus and idiomatic Rust, meaning Atom.rs state management will be faster, more efficient, and less sensitive to data races than Redux-style apps.
Internally, Dioxus uses batching to speed up linear-style operations. Recoil.rs integrates with this batching optimization, making app-wide changes extremely fast. This way, Recoil.rs can be pushed significantly harder than Redux without the need to enable/disable debug flags to prevent performance slowdowns.
Internally, Dioxus uses batching to speed up linear-style operations. Atom.rs integrates with this batching optimization, making app-wide state changes extremely fast. This way, Atom.rs can be pushed significantly harder than Redux without the need to enable/disable debug flags to prevent performance slowdowns.
# Guide
@@ -16,16 +16,16 @@ A simple atom of state is defined globally as a const:
const Light: Atom<&str> = |_| "Green";
```
This atom of state is initialized with a value of `"Green"`. The atom that is returned does not actually contain any values. Instead, the atom's key - which is automatically generated in this instance - is used in the context of a Recoil App.
This atom of state is initialized with a value of `"Green"`. The atom that is returned does not actually contain any values. Instead, the atom's key - which is automatically generated in this instance - is used in the context of a Atom App.
This is then later used in components like so:
```rust
fn App(ctx: Context<()>) -> VNode {
// The recoil root must be initialized at the top of the application before any use_recoil hooks
recoil::init_recoil_root(&ctx, |_| {});
// The Atom root must be initialized at the top of the application before any use_Atom hooks
Atoms::init_Atom_root(&ctx, |_| {});
let color = recoil::use_read(ctx, Light);
let color = Atoms::use_read(ctx, Light);
ctx.render(rsx!{
h1 {"Color of light: {color}"}
@@ -37,8 +37,8 @@ Atoms are considered "Writable" objects since any consumer may also set the Atom
```rust
fn App(ctx: Context<()>) -> VNode {
let color = recoil::use_read(ctx, Light);
let set_color = recoil::use_write(ctx, Light);
let color = Atoms::use_read(ctx, Light);
let set_color = Atoms::use_write(ctx, Light);
rsx!{in ctx,
div {
h1{"Color: {color}"}
@@ -54,7 +54,7 @@ fn App(ctx: Context<()>) -> VNode {
```rust
fn App(ctx: Context<()>) -> VNode {
let (color, set_color) = recoil::use_read_write(ctx, Light);
let (color, set_color) = Atoms::use_read_write(ctx, Light);
rsx!{in ctx,
div {
h1{"Color: {color}"}
@@ -68,9 +68,9 @@ fn App(ctx: Context<()>) -> VNode {
Selectors are a concept popular in the JS world as a way of narrowing down a selection of state outside the VDOM lifecycle. Selectors have two functions: 1) summarize/narrow down some complex state and 2) memoize calculations.
Selectors are only `readable` - they cannot be set. This differs from RecoilJS where selectors _are_ `writable`. Selectors, as you might've guessed, "select" some data from atoms and other selectors.
Selectors are only `readable` - they cannot be set. This differs from AtomJS where selectors _are_ `writable`. Selectors, as you might've guessed, "select" some data from atoms and other selectors.
Selectors provide a `SelectorApi` which essentially exposes a read-only `RecoilApi`. They have the `get` method which allows any readable valued to be obtained for the purpose of generating a new value. A `Selector` may only return `'static` data, however `SelectorBorrowed` may return borrowed data.
Selectors provide a `SelectorApi` which essentially exposes a read-only `AtomApi`. They have the `get` method which allows any readable valued to be obtained for the purpose of generating a new value. A `Selector` may only return `'static` data, however `SelectorBorrowed` may return borrowed data.
returning static data:
@@ -104,7 +104,7 @@ Returning borrowed data is generally frowned upon, but may be useful when used w
- If a selected value equals its previous selection (via PartialEq), the old value must be kept around to avoid evaluating subscribed components.
- It's unlikely that a change in a dependency's data will not change the selector's output.
In general, borrowed selectors introduce a slight memory overhead as they need to retain previous state to safely memoize downstream subscribers. The amount of state kept around scales with the amount of `gets` in a selector - though as the number of dependencies increase, the less likely the selector actually stays memoized. Recoil tries to optimize this behavior the best it can to balance component evaluations with memory overhead.
In general, borrowed selectors introduce a slight memory overhead as they need to retain previous state to safely memoize downstream subscribers. The amount of state kept around scales with the amount of `gets` in a selector - though as the number of dependencies increase, the less likely the selector actually stays memoized. Atom tries to optimize this behavior the best it can to balance component evaluations with memory overhead.
## Families
@@ -126,7 +126,7 @@ Whenever you `select` on a `Family`, the ID of the entry is tracked. Other subsc
```rust
fn App(ctx: Context<()>) -> VNode {
let (rating, set_rating) = recoil::use_read_write(ctx, CloudRatings.select("AWS"));
let (rating, set_rating) = Atoms::use_read_write(ctx, CloudRatings.select("AWS"));
rsx!{in ctx,
div {
h1{ "AWS rating: {rating}" }
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
+28
View File
@@ -0,0 +1,28 @@
use dioxus::virtual_dom::VirtualDom;
use dioxus_core::prelude::*;
fn main() {
let mut dom = VirtualDom::new(App);
let edits = dom.rebuild().unwrap();
dbg!(edits);
}
static App: FC<()> = |ctx| {
//
ctx.render(rsx! {
div {
"abc"
"123"
}
})
};
static Fragment: FC<()> = |ctx| {
//
let children = ctx.children();
ctx.render(LazyNodes::new(move |c: &NodeCtx| {
//
let frag = c.bump().alloc(VFragment::new(None, children));
VNode::Fragment(frag)
}))
};
+28
View File
@@ -33,3 +33,31 @@ impl EmptyBuilder {
pub fn fc_to_builder<T: Properties>(_: FC<T>) -> T::Builder {
T::builder()
}
mod eliminate_bounds {
struct BNode<'a> {
_p: &'a (),
}
trait Broper {}
impl Broper for () {}
struct Bontext<'a, P: Broper + 'a> {
inner: &'a (),
props: P,
}
fn Bexample(c: Bontext<()>) -> BNode {
todo!()
}
struct MyBrops<'a> {
_i: &'a (),
}
impl<'a> Broper for MyBrops<'a> {}
fn Bexample2<'a>(c: Bontext<'a, MyBrops<'a>>) -> BNode {
todo!()
}
}
+26 -25
View File
@@ -1,3 +1,14 @@
//! This module contains the stateful DiffMachine and all methods to diff VNodes, their properties, and their children.
//!
//! Notice:
//! ------
//!
//! The inspiration and code for this module was originally taken from Dodrio (@fitzgen) and modified to support Components,
//! Fragments, Suspense, and additional batching operations.
//!
//! Implementation Details:
//! -----------------------
//!
//! Diff the `old` node with the `new` node. Emits instructions to modify a
//! physical DOM node that reflects `old` into something that reflects `new`.
//!
@@ -8,39 +19,18 @@
//!
//! The change list stack is in the same state when this function exits.
//!
//! ----
//! Further Reading and Thoughts
//! ----------------------------
//!
//! There are more ways of increasing diff performance here that are currently not implemented.
//! Additionally, the caching mechanism has also been tweaked.
//!
//! Instead of having "cached" nodes, each component is, by default, a cached node. This leads to increased
//! memory overhead for large numbers of small components, but we can optimize this by tracking alloc size over time
//! and shrinking bumps down if possible.
//!
//! Additionally, clean up of these components is not done at diff time (though it should), but rather, the diffing
//! proprogates removal lifecycle events for affected components into the event queue. It's not imperative that these
//! are ran immediately, but it should be noted that cleanup of components might be able to emit changes.
//!
//! This diffing only ever occurs on a component-by-component basis (not entire trees at once).
//!
//! Currently, the listener situation is a bit broken.
//! We aren't removing listeners (choosing to leak them instead) :(
//! Eventually, we'll set things up so add/remove listener is an instruction again
//!
//! A major assumption of this diff algorithm when combined with the ChangeList is that the Changelist will be
//! fresh and the event queue is clean. This lets us continue to batch edits together under the same ChangeList
//!
//! More info on how to improve this diffing algorithm:
//! - https://hacks.mozilla.org/2019/03/fast-bump-allocated-virtual-doms-with-rust-and-wasm/
use crate::{arena::ScopeArena, innerlude::*};
use bumpalo::Bump;
use fxhash::{FxHashMap, FxHashSet};
use generational_arena::Arena;
use std::{
cell::{RefCell, RefMut},
cmp::Ordering,
collections::VecDeque,
rc::{Rc, Weak},
sync::atomic::AtomicU32,
};
@@ -207,7 +197,18 @@ impl<'a> DiffMachine<'a> {
todo!("Fragments not currently supported in diffing")
}
(_, VNode::Fragment(_)) => {
(VNode::Fragment(_), VNode::Fragment(_)) => {
todo!("Fragments not currently supported in diffing")
}
(old_n, VNode::Fragment(_)) => {
match old_n {
VNode::Element(_) => todo!(),
VNode::Text(_) => todo!(),
VNode::Fragment(_) => todo!(),
VNode::Suspended => todo!(),
VNode::Component(_) => todo!(),
}
todo!("Fragments not currently supported in diffing")
}
}
+2 -65
View File
@@ -1,72 +1,11 @@
//! <div align="center">
//! <h1>🌗🚀 📦 Dioxus</h1>
//! <p>
//! <strong>A concurrent, functional, virtual DOM for Rust</strong>
//! </p>
//! </div>
//! Dioxus: a concurrent, functional, reactive virtual dom for any renderer in Rust.
//! Dioxus Core
//! ----------
//!
//! This crate aims to maintain a hook-based, renderer-agnostic framework for cross-platform UI development.
//!
//! ## Components
//! The base unit of Dioxus is the `component`. Components can be easily created from just a function - no traits required:
//! ```
//! use dioxus_core::prelude::*;
//!
//! #[derive(Properties)]
//! struct Props { name: String }
//!
//! fn Example(ctx: Context, props: &Props) -> VNode {
//! html! { <div> "Hello {ctx.name}!" </div> }
//! }
//! ```
//! Components need to take a "Context" parameter which is generic over some properties. This defines how the component can be used
//! and what properties can be used to specify it in the VNode output. Component state in Dioxus is managed by hooks - if you're new
//! to hooks, check out the hook guide in the official guide.
//!
//! Components can also be crafted as static closures, enabling type inference without all the type signature noise:
//! ```
//! use dioxus_core::prelude::*;
//!
//! #[derive(Properties)]
//! struct Props { name: String }
//!
//! static Example: FC<Props> = |ctx| {
//! html! { <div> "Hello {ctx.name}!" </div> }
//! }
//! ```
//!
//! If the properties struct is too noisy for you, we also provide a macro that converts variadic functions into components automatically.
//! Many people don't like the magic of proc macros, so this is entirely optional. Under-the-hood, we simply transplant the
//! function arguments into a struct, so there's very little actual magic happening.
//!
//! ```
//! use dioxus_core::prelude::*;
//!
//! #[derive_props]
//! static Example: FC = |ctx, name: &String| {
//! html! { <div> "Hello {name}!" </div> }
//! }
//! ```
//!
//! ## Hooks
//! Dioxus uses hooks for state management. Hooks are a form of state persisted between calls of the function component. Instead of
//! using a single struct to store data, hooks use the "use_hook" building block which allows the persistence of data between
//! function component renders. Each hook stores some data in a "memory cell" and needs to be called in a consistent order.
//! This means hooks "anything with `use_x`" may not be called conditionally.
//!
//! This allows functions to reuse stateful logic between components, simplify large complex components, and adopt more clear context
//! subscription patterns to make components easier to read.
//!
//! ## Supported Renderers
//! Instead of being tightly coupled to a platform, browser, or toolkit, Dioxus implements a VirtualDOM object which
//! can be consumed to draw the UI. The Dioxus VDOM is reactive and easily consumable by 3rd-party renderers via
//! the `Patch` object. See [Implementing a Renderer](docs/8-custom-renderer.md) and the `StringRenderer` classes for information
//! on how to implement your own custom renderer. We provide 1st-class support for these renderers:
//! - dioxus-desktop (via WebView)
//! - dioxus-web (via WebSys)
//! - dioxus-ssr (via StringRenderer)
//! - dioxus-liveview (SSR + StringRenderer)
//!
pub mod arena;
@@ -104,7 +43,6 @@ pub(crate) mod innerlude {
pub type FC<P> = fn(Context<P>) -> VNode;
// Re-export the FC macro
pub use crate as dioxus;
pub use crate::nodebuilder as builder;
pub use dioxus_core_macro::{html, rsx};
}
@@ -131,7 +69,6 @@ pub mod prelude {
pub use bumpalo::Bump;
// Re-export the FC macro
pub use crate as dioxus;
pub use crate::nodebuilder as builder;
// pub use dioxus_core_macro::fc;
+5 -33
View File
@@ -251,87 +251,61 @@ impl<'a> EditMachine<'a> {
pub fn push_temporary(&mut self, temp: u32) {
debug_assert!(self.traversal_is_committed());
// debug!("emit: push_temporary({})", temp);
self.emitter.push(Edit::PushTemporary { temp });
// self.emitter.push_temporary(temp);
}
pub fn remove_child(&mut self, child: usize) {
debug_assert!(self.traversal_is_committed());
// debug!("emit: remove_child({})", child);
// self.emitter.remove_child(child as u32);
self.emitter.push(Edit::RemoveChild { n: child as u32 })
}
pub fn insert_before(&mut self) {
debug_assert!(self.traversal_is_committed());
// debug!("emit: insert_before()");
// self.emitter.insert_before();
self.emitter.push(Edit::InsertBefore {})
}
pub fn set_text(&mut self, text: &'a str) {
debug_assert!(self.traversal_is_committed());
// debug!("emit: set_text({:?})", text);
// self.emitter.set_text(text);
self.emitter.push(Edit::SetText { text });
// .set_text(text.as_ptr() as u32, text.len() as u32);
}
pub fn remove_self_and_next_siblings(&mut self) {
debug_assert!(self.traversal_is_committed());
// debug!("emit: remove_self_and_next_siblings()");
self.emitter.push(Edit::RemoveSelfAndNextSiblings {});
// self.emitter.remove_self_and_next_siblings();
}
pub fn replace_with(&mut self) {
debug_assert!(self.traversal_is_committed());
// debug!("emit: replace_with()");
self.emitter.push(Edit::ReplaceWith {});
// if let Some(id) = self.current_known {
// // update mapping
// self.emitter.push(Edit::MakeKnown{node: id});
// self.current_known = None;
// }
// self.emitter.replace_with();
self.emitter.push(Edit::ReplaceWith {});
}
pub fn set_attribute(&mut self, name: &'a str, value: &'a str, is_namespaced: bool) {
debug_assert!(self.traversal_is_committed());
// todo!()
if name == "class" && !is_namespaced {
// let class_id = self.ensure_string(value);
// let class_id = self.ensure_string(value);
// debug!("emit: set_class({:?})", value);
// self.emitter.set_class(class_id.into());
self.emitter.push(Edit::SetClass { class_name: value });
} else {
self.emitter.push(Edit::SetAttribute { name, value });
// let name_id = self.ensure_string(name);
// let value_id = self.ensure_string(value);
// debug!("emit: set_attribute({:?}, {:?})", name, value);
// self.state
// .emitter
// .set_attribute(name_id.into(), value_id.into());
}
}
pub fn remove_attribute(&mut self, name: &'a str) {
// todo!("figure out how to get this working with ensure string");
self.emitter.push(Edit::RemoveAttribute { name });
// self.emitter.remove_attribute(name);
// debug_assert!(self.traversal_is_committed());
// // debug!("emit: remove_attribute({:?})", name);
// let name_id = self.ensure_string(name);
// self.emitter.remove_attribute(name_id.into());
}
pub fn append_child(&mut self) {
debug_assert!(self.traversal_is_committed());
// debug!("emit: append_child()");
self.emitter.push(Edit::AppendChild {});
// self.emitter.append_child();
}
pub fn create_text_node(&mut self, text: &'a str) {
@@ -378,7 +352,6 @@ impl<'a> EditMachine<'a> {
pub fn remove_event_listener(&mut self, event: &'a str) {
debug_assert!(self.traversal_is_committed());
self.emitter.push(Edit::RemoveListener { event });
// debug!("emit: remove_event_listener({:?})", event);
}
pub fn save_known_root(&mut self, id: u32) {
@@ -388,7 +361,6 @@ impl<'a> EditMachine<'a> {
pub fn load_known_root(&mut self, id: u32) {
log::debug!("emit: TraverseToKnown({:?})", id);
// self.current_known = Some(id);
self.emitter.push(Edit::TraverseToKnown { node: id })
}
}
-13
View File
@@ -1,13 +0,0 @@
[package]
name = "dioxus"
version = "0.1.0"
authors = ["Jonathan Kelley <jkelleyrtp@gmail.com>"]
edition = "2018"
description = "Core functionality for Dioxus - a concurrent renderer-agnostic Virtual DOM for interactive user experiences"
"license" = "MIT/Apache-2.0"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
dioxus-core = { path = "../core", version = "0.1.0" }
dioxus-core-macro = { path = "../core-macro", version = "0.1.0" }
dioxus-hooks = { path = "../hooks", version = "0.0.0" }
-51
View File
@@ -1,51 +0,0 @@
# Dioxus
This crate provides all the batteries required to build Dioxus apps.
Included in this crate is:
- Dioxus core
- Essential hooks (use_state, use_ref, use_reducer, etc)
- rsx! and html! macros
You'll still need to pull in a renderer to render the Dioxus VDOM. Any one of:
- dioxus-web (to run in WASM)
- dioxus-ssr (to run on the server or for static sites)
- dioxus-webview (to run on the desktop)
- dioxus-mobile (to run on iOS/Android)
Make sure dioxus and its renderer share the same major version; the renderers themselves rely on dioxus.
```toml
[dependencies]
dioxus = "0.2"
dioxus-web = "0.2"
```
```rust
use dioxus::*;
fn main() {
dioxus_web::start(|ctx| {
rsx!{in ctx, div { "Hello world" }}
})
}
```
Additionally, you'll want to look at other projects for more batteries
- essential-hooks (use_router, use_storage, use_cache, use_channel)
- Recoil.rs or Reducer.rs for state management
- 3D renderer (ThreeD), charts (Sciviz), game engine (Bevy)
Extra resources:
- The guide is available at:
- The crate docs are at:
- Video tutorials are at:
- Examples are at:
- Full clones are at:
- Community at: [www.reddit.com/r/dioxus]()
Happy building!
-9
View File
@@ -1,9 +0,0 @@
pub mod prelude {
pub use dioxus_core::prelude::*;
pub use dioxus_core_macro::fc;
}
// use dioxus_core::prelude::FC;
// Re-export core completely
pub use dioxus_core as core;
+3 -3
View File
@@ -7,7 +7,7 @@ edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
dioxus-ssr = { path = "../ssr" }
dioxus-ssr = { path="../ssr" }
pulldown-cmark = "0.8.0"
recoil = { path = "../recoil" }
codeblocks = { path = "../../../ecosystem-dioxus/syntec-dioxus/" }
atoms = { path="../atoms" }
codeblocks = { path="../../../ecosystem-dioxus/syntec-dioxus/" }
+3 -3
View File
@@ -8,7 +8,7 @@ license = "MIT/Apache-2.0"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
dioxus-core = { path = "../core", version = "0.1.2" }
dioxus-core = { path="../core", version="0.1.2" }
js-sys = "0.3"
wasm-bindgen = "0.2.71"
lazy_static = "1.4.0"
@@ -21,7 +21,7 @@ console_error_panic_hook = "0.1.6"
generational-arena = "0.2.8"
wasm-bindgen-test = "0.3.21"
once_cell = "1.7.2"
recoil = { path = "../recoil" }
atoms = { path="../atoms" }
# wasm-bindgen = "0.2.70"
# futures = "0.3.12"
@@ -75,7 +75,7 @@ opt-level = 's'
crate-type = ["cdylib", "rlib"]
[dev-dependencies]
uuid = { version = "0.8.2", features = ["v4", "wasm-bindgen"] }
uuid = { version="0.8.2", features=["v4", "wasm-bindgen"] }
[[example]]
name = "todomvc"
+38 -26
View File
@@ -6,7 +6,6 @@ use dioxus_core::{
prelude::ScopeIdx,
};
use fxhash::FxHashMap;
use log::debug;
use wasm_bindgen::{closure::Closure, JsCast};
use web_sys::{window, Document, Element, Event, HtmlInputElement, HtmlOptionElement, Node};
@@ -38,6 +37,16 @@ pub(crate) struct PatchMachine {
pub(crate) events: EventDelegater,
pub(crate) current_known: Option<u32>,
// We need to make sure to add comments between text nodes
// We ensure that the text siblings are patched by preventing the browser from merging
// neighboring text nodes. Originally inspired by some of React's work from 2016.
// -> https://reactjs.org/blog/2016/04/07/react-v15.html#major-changes
// -> https://github.com/facebook/react/pull/5753
//
// `ptns` = Percy text node separator
// TODO
pub(crate) last_node_was_text: bool,
}
#[derive(Debug)]
@@ -146,8 +155,6 @@ impl Stack {
pub fn pop(&mut self) -> Node {
let res = self.list.pop().unwrap();
// debug!("stack-pop: {:?}", res);
res
}
@@ -156,10 +163,6 @@ impl Stack {
}
pub fn top(&self) -> &Node {
// log::info!(
// "Called top of stack with {} items remaining",
// self.list.len()
// );
match self.list.last() {
Some(a) => a,
None => panic!("Called 'top' of an empty stack, make sure to push the root first"),
@@ -185,13 +188,13 @@ impl PatchMachine {
stack: Stack::with_capacity(20),
temporaries: Default::default(),
document,
last_node_was_text: false,
}
}
pub fn unmount(&mut self) {
self.stack.clear();
self.temporaries.clear();
// self.templates.clear();
}
pub fn start(&mut self) {
@@ -205,11 +208,6 @@ impl PatchMachine {
self.temporaries.clear();
}
pub fn get_template(&self, id: CacheId) -> Option<&Node> {
todo!()
// self.templates.get(&id)
}
pub fn handle_edit(&mut self, edit: &Edit) {
match *edit {
// 0
@@ -276,11 +274,7 @@ impl PatchMachine {
Edit::SetAttribute { name, value } => {
let node = self.stack.top();
log::debug!("setting attribute for element");
if let Some(node) = node.dyn_ref::<web_sys::Element>() {
// node.set_attribute(name, value).unwrap();
log::info!("setting attr {} {}", name, value);
node.set_attribute(name, value).unwrap();
}
if let Some(node) = node.dyn_ref::<HtmlInputElement>() {
@@ -296,13 +290,10 @@ impl PatchMachine {
}
if let Some(node) = node.dyn_ref::<HtmlOptionElement>() {
log::debug!("el is html option element");
if name == "selected" {
node.set_selected(true);
}
}
log::debug!("el is none");
// if let Some(node) = node.dyn_ref::<web_sys::Namesp>() {}
}
// 4
@@ -357,15 +348,35 @@ impl PatchMachine {
}
// 9
Edit::CreateTextNode { text } => self.stack.push(
self.document
.create_text_node(text)
.dyn_into::<Node>()
.unwrap(),
),
Edit::CreateTextNode { text } => {
//
// We ensure that the text siblings are patched by preventing the browser from merging
// neighboring text nodes. Originally inspired by some of React's work from 2016.
// -> https://reactjs.org/blog/2016/04/07/react-v15.html#major-changes
// -> https://github.com/facebook/react/pull/5753
//
// `ptns` = Percy text node separator
// TODO
if self.last_node_was_text {
let n = self
.document
.create_comment("ptns")
.dyn_into::<Node>()
.unwrap();
}
self.stack.push(
self.document
.create_text_node(text)
.dyn_into::<Node>()
.unwrap(),
);
self.last_node_was_text = true;
}
// 10
Edit::CreateElement { tag_name } => {
self.last_node_was_text = false;
let el = self
.document
.create_element(tag_name)
@@ -438,6 +449,7 @@ impl PatchMachine {
// 14
Edit::CreateElementNs { tag_name, ns } => {
self.last_node_was_text = false;
let el = self
.document
.create_element_ns(Some(ns), tag_name)