WIP: Todomvc in progress

This commit is contained in:
Jonathan Kelley committed 2021-05-19 10:14:02 -04:00
1 parent 0d44f009b0
commit b843dbd367
10 files changed
+416 -219

No files matched your search

+48 -19
View File
@@ -17,12 +17,31 @@ use {
// Parse any stream coming from the rsx! macro
// ==============================================
pub struct RsxRender {
// custom_context: Option<Ident>,
custom_context: Option<Ident>,
root: AmbiguousElement,
}
impl Parse for RsxRender {
fn parse(input: ParseStream) -> Result<Self> {
// try to parse the first ident and comma
let custom_context =
if input.peek(Token![in]) && input.peek2(Ident) && input.peek3(Token![,]) {
let _ = input.parse::<Token![in]>()?;
let name = input.parse::<Ident>()?;
if is_valid_html_tag(&name.to_string()) {
return Err(Error::new(
input.span(),
"Custom context cannot be an html element name",
));
} else {
input.parse::<Token![,]>().unwrap();
Some(name)
}
} else {
// todo!("NOT WORKIGN");
None
};
let root = { input.parse::<AmbiguousElement>() }?;
if !input.is_empty() {
return Err(Error::new(
@@ -31,7 +50,10 @@ impl Parse for RsxRender {
));
}
Ok(Self { root })
Ok(Self {
root,
custom_context,
})
}
}
@@ -39,22 +61,29 @@ impl ToTokens for RsxRender {
fn to_tokens(&self, out_tokens: &mut TokenStream2) {
// create a lazy tree that accepts a bump allocator
// Currently disabled
//
// let final_tokens = match &self.custom_context {
// Some(ident) => quote! {
// #ident.render(dioxus::prelude::LazyNodes::new(move |ctx|{
// let bump = ctx.bump;
// #new_toks
// }))
// },
let inner = &self.root;
let output = quote! {
dioxus::prelude::LazyNodes::new(move |ctx|{
let bump = &ctx.bump();
#inner
})
let output = match &self.custom_context {
Some(ident) => {
//
quote! {
#ident.render(dioxus::prelude::LazyNodes::new(move |__ctx|{
let bump = &__ctx.bump();
#inner
}))
}
}
None => {
quote! {
dioxus::prelude::LazyNodes::new(move |__ctx|{
let bump = &__ctx.bump();
#inner
})
}
}
};
output.to_tokens(out_tokens)
}
}
@@ -237,7 +266,7 @@ impl ToTokens for &Component {
};
let _toks = tokens.append_all(quote! {
dioxus::builder::virtual_child(ctx, #name, #builder, #key_token)
dioxus::builder::virtual_child(__ctx, #name, #builder, #key_token)
});
}
}
@@ -327,7 +356,7 @@ impl ToTokens for &Element {
let name = &self.name.to_string();
tokens.append_all(quote! {
dioxus::builder::ElementBuilder::new(ctx, #name)
dioxus::builder::ElementBuilder::new(__ctx, #name)
});
for attr in self.attrs.iter() {
@@ -448,7 +477,7 @@ impl ToTokens for &ElementAttr {
}
AttrType::Event(event) => {
tokens.append_all(quote! {
.add_listener(dioxus::events::on::#nameident(ctx, #event))
.add_listener(dioxus::events::on::#nameident(__ctx, #event))
});
}
AttrType::FieldTokens(exp) => {
@@ -459,7 +488,7 @@ impl ToTokens for &ElementAttr {
AttrType::EventTokens(event) => {
//
tokens.append_all(quote! {
.add_listener(dioxus::events::on::#nameident(ctx, #event))
.add_listener(dioxus::events::on::#nameident(__ctx, #event))
})
}
}
+25 -1
View File
@@ -887,6 +887,7 @@ impl<'a> DiffMachine<'a> {
}
match old.len().cmp(&new.len()) {
// old.len > new.len -> removing some nodes
Ordering::Greater => {
// [... parent prev_child]
self.change_list.go_to_sibling(new.len());
@@ -896,6 +897,7 @@ impl<'a> DiffMachine<'a> {
self.remove_self_and_next_siblings(&old[new.len()..]);
// [... parent]
}
// old.len < new.len -> adding some nodes
Ordering::Less => {
// [... parent last_child]
self.change_list.go_up();
@@ -903,6 +905,7 @@ impl<'a> DiffMachine<'a> {
self.change_list.commit_traversal();
self.create_and_append_children(&new[old.len()..]);
}
// old.len == new.len -> no nodes added/removed, but πerhaps changed
Ordering::Equal => {
// [... parent child]
self.change_list.go_up();
@@ -924,6 +927,7 @@ impl<'a> DiffMachine<'a> {
// When this function returns, the change list stack is in the same state.
pub fn remove_all_children(&mut self, old: &[VNode<'a>]) {
debug_assert!(self.change_list.traversal_is_committed());
log::debug!("REMOVING CHILDREN");
for _child in old {
// registry.remove_subtree(child);
}
@@ -958,7 +962,27 @@ impl<'a> DiffMachine<'a> {
// [... parent]
pub fn remove_self_and_next_siblings(&mut self, old: &[VNode<'a>]) {
debug_assert!(self.change_list.traversal_is_committed());
for _child in old {
for child in old {
if let VNode::Component(vcomp) = child {
// self.change_list
// .create_text_node("placeholder for vcomponent");
let root_id = vcomp.stable_addr.as_ref().borrow().unwrap();
self.lifecycle_events.push_back(LifeCycleEvent::Remove {
root_id,
stable_scope_addr: Rc::downgrade(&vcomp.ass_scope),
})
// let id = get_id();
// *component.stable_addr.as_ref().borrow_mut() = Some(id);
// self.change_list.save_known_root(id);
// let scope = Rc::downgrade(&component.ass_scope);
// self.lifecycle_events.push_back(LifeCycleEvent::Mount {
// caller: Rc::downgrade(&component.caller),
// root_id: id,
// stable_scope_addr: scope,
// });
}
// registry.remove_subtree(child);
}
self.change_list.remove_self_and_next_siblings();
+36 -4
View File
@@ -26,7 +26,7 @@ use std::{
any::{Any, TypeId},
borrow::{Borrow, BorrowMut},
cell::RefCell,
collections::{HashMap, HashSet},
collections::{HashMap, HashSet, VecDeque},
fmt::Debug,
future::Future,
pin::Pin,
@@ -277,7 +277,7 @@ impl VirtualDom {
// Start a new mutable borrow to components
// We are guaranteeed that this scope is unique because we are tracking which nodes have modified
let mut cur_component = self.components.try_get_mut(update.idx).unwrap();
let cur_component = self.components.try_get_mut(update.idx).unwrap();
cur_component.run_scope()?;
@@ -323,7 +323,12 @@ impl VirtualDom {
})
})?;
cur_component.children.borrow_mut().insert(idx);
{
let cur_component = self.components.try_get_mut(update.idx).unwrap();
let mut ch = cur_component.children.borrow_mut();
ch.insert(idx);
std::mem::drop(ch);
}
// Grab out that component
let new_component = self.components.try_get_mut(idx).unwrap();
@@ -415,8 +420,33 @@ impl VirtualDom {
root_id,
stable_scope_addr,
} => {
unimplemented!("This feature (Remove) is unimplemented")
let id = stable_scope_addr
.upgrade()
.unwrap()
.as_ref()
.borrow()
.unwrap();
log::warn!("Removing node {:#?}", id);
// This would normally be recursive but makes sense to do linear to
let mut children_to_remove = VecDeque::new();
children_to_remove.push_back(id);
// Accumulate all the child components that need to be removed
while let Some(child_id) = children_to_remove.pop_back() {
let comp = self.components.try_get(child_id).unwrap();
let children = comp.children.borrow();
for child in children.iter() {
children_to_remove.push_front(*child);
}
log::debug!("Removing component: {:#?}", child_id);
self.components
.with(|components| components.remove(child_id).unwrap())
.unwrap();
}
}
LifeCycleEvent::Replace {
caller,
root_id: id,
@@ -442,6 +472,8 @@ pub struct Scope {
// The parent's scope ID
pub parent: Option<ScopeIdx>,
// IDs of children that this scope has created
// This enables us to drop the children and their children when this scope is destroyed
pub children: RefCell<HashSet<ScopeIdx>>,
// A reference to the list of components.
+4 -2
View File
@@ -1,9 +1,11 @@
[package]
name = "dioxus-recoil"
name = "recoil"
version = "0.0.0"
authors = ["Jonathan Kelley <jkelleyrtp@gmail.com>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
[dependencies]
dioxus-core = { path = "../core" }
uuid = "0.8.2"
+8 -4
View File
@@ -1,13 +1,17 @@
# Recoil.rs
# Recoil.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 and pulls in
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.
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.
## Guide
A simple atom of state is defined globally as a const:
A simple atom of state is defined globally as a static:
```rust
static Light: Atom<&'static str> = atom(|_| "Green");
const Light: Atom<&'static str> = atom(|_| "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.
@@ -16,7 +20,7 @@ This is then later used in components like so:
```rust
fn App(ctx: Context, props: &()) -> DomTree {
// The recoil root must be initialized at the top of the application before any uses
// The recoil root must be initialized at the top of the application before any use_recoils
recoil::init_recoil_root(&ctx, |_| {});
let color = use_recoil(&ctx, &TITLE);
+130 -5
View File
@@ -1,7 +1,132 @@
#[cfg(test)]
mod tests {
#[test]
fn it_works() {
assert_eq!(2 + 2, 4);
// ========================
// Important hooks
// ========================
pub fn init_recoil_root(ctx: Context) {}
pub fn use_recoil_value() {}
pub fn use_recoil() {}
pub fn use_set_recoil() {}
use dioxus_core::virtual_dom::Context;
pub struct RecoilContext {}
impl RecoilContext {
/// Get the value of an atom. Returns a reference to the underlying data.
pub fn get<T: PartialEq>(&self, t: &'static Atom<T>) -> &T {
todo!()
}
/// Replace an existing value with a new value
///
/// This does not replace the value instantly.
/// All calls to "get" will return the old value until the component is rendered.
pub fn set<T: PartialEq, O>(&self, t: &'static Atom<T>, new: T) {
self.modify(t, move |old| *old = new);
}
/// Modify lets you modify the value in place. However, because there's no previous value around to compare
/// the new one with, we are unable to memoize the change. As such, all downsteam users of this Atom will
/// be updated, causing all subsrcibed components to re-render.
///
/// This is fine for most values, but might not be performant when dealing with collections. For collections,
/// use the "Family" variants as these will stay memoized for inserts, removals, and modifications.
///
/// Note - like "set" this won't propogate instantly. Once all "gets" are dropped, only then will the update occur
pub fn modify<T: PartialEq, O>(&self, t: &'static Atom<T>, f: impl FnOnce(&mut T) -> O) -> O {
todo!()
}
}
pub fn use_recoil_context<T>(c: Context) -> &T {
todo!()
}
// pub fn use_callback<'a>(c: &Context<'a>, f: impl Fn() -> G) -> &'a RecoilContext {
// todo!()
// }
pub fn use_atom<'a, T: PartialEq>(c: Context<'a>, t: &'static Atom<T>) -> &'a T {
todo!()
}
pub trait Readable {}
impl<T: PartialEq> Readable for &'static Atom<T> {}
impl<K: PartialEq, V: PartialEq> Readable for &'static AtomFamily<K, V> {}
pub fn use_atom_family<'a, K: PartialEq, V: PartialEq>(
c: &Context<'a>,
t: &'static AtomFamily<K, V>,
g: K,
) -> &'a V {
todo!()
}
pub struct AtomBuilder<T: PartialEq> {
pub key: String,
pub manual_init: Option<Box<dyn Fn() -> T>>,
_never: std::marker::PhantomData<T>,
}
impl<T: PartialEq> AtomBuilder<T> {
pub fn new() -> Self {
Self {
key: "".to_string(),
manual_init: None,
_never: std::marker::PhantomData {},
}
}
pub fn init<A: Fn() -> T + 'static>(&mut self, f: A) {
self.manual_init = Some(Box::new(f));
}
pub fn set_key(&mut self, _key: &'static str) {}
}
// =====================================
// Atom
// =====================================
pub struct atom<T: PartialEq>(pub fn(&mut AtomBuilder<T>) -> T);
pub type Atom<T: PartialEq> = atom<T>;
// =====================================
// Atom Family
// =====================================
pub struct AtomFamilyBuilder<K, V> {
_never: std::marker::PhantomData<(K, V)>,
}
pub struct atom_family<K: PartialEq, V: PartialEq>(pub fn(&mut AtomFamilyBuilder<K, V>));
pub type AtomFamily<K: PartialEq, V: PartialEq> = atom_family<K, V>;
// =====================================
// Selectors
// =====================================
pub struct SelectorApi {}
impl SelectorApi {
pub fn get<T: PartialEq>(&self, t: &'static Atom<T>) -> &T {
todo!()
}
}
// pub struct SelectorBuilder<Out, const Built: bool> {
// _p: std::marker::PhantomData<Out>,
// }
// impl<O> SelectorBuilder<O, false> {
// pub fn getter(self, f: impl Fn(()) -> O) -> SelectorBuilder<O, true> {
// todo!()
// // std::rc::Rc::pin(value)
// // todo!()
// }
// }
pub struct selector<O>(pub fn(&SelectorApi) -> O);
// pub struct selector<O>(pub fn(SelectorBuilder<O, false>) -> SelectorBuilder<O, true>);
pub type Selector<O> = selector<O>;
pub fn use_selector<'a, O>(c: Context<'a>, s: &'static Selector<O>) -> &'a O {
todo!()
}
+1
View File
@@ -21,6 +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" }
# wasm-bindgen = "0.2.70"
# futures = "0.3.12"
+43 -1
View File
@@ -1,4 +1,46 @@
use dioxus::{events::on::MouseEvent, prelude::*};
use dioxus_core as dioxus;
use dioxus_web::WebsysRenderer;
fn main() {}
fn main() {
wasm_logger::init(wasm_logger::Config::new(log::Level::Debug));
console_error_panic_hook::set_once();
wasm_bindgen_futures::spawn_local(WebsysRenderer::start(App));
}
fn App(ctx: Context, props: &()) -> DomTree {
let cansee = use_state_new(&ctx, || false);
rsx! { in ctx,
div {
"Shadow of the child:"
button {
"Gaze into the void"
onclick: move |_| cansee.set(!**cansee)
}
{cansee.then(|| rsx!{ Child {} })}
}
}
}
fn Child(ctx: Context, props: &()) -> DomTree {
rsx! { in ctx,
section { class: "py-6 bg-coolGray-100 text-coolGray-900"
div { class: "container mx-auto flex flex-col items-center justify-center p-4 space-y-8 md:p-10 md:px-24 xl:px-48"
h1 { class: "text-5xl font-bold leading-none text-center",
"Sign up now"
}
p { class: "text-xl font-medium text-center",
"At a assumenda quas cum earum ut itaque commodi saepe rem aspernatur quam natus quis nihil quod, hic explicabo doloribus magnam neque, exercitationem eius sunt!"
}
div { class: "flex flex-col space-y-4 sm:space-y-0 sm:flex-row sm:space-x-8"
button { class: "px-8 py-3 text-lg font-semibold rounded bg-violet-600 text-coolGray-50",
"Get started"
}
button { class: "px-8 py-3 text-lg font-normal border rounded bg-coolGray-800 text-coolGray-50 border-coolGray-700",
"Learn more"
}
}
}
}
}
}
+120 -182
View File
@@ -1,31 +1,114 @@
//! Example: TODOVMC - One file
//! ---------------------------
//! This example shows how to build a one-file TODO MVC app with Dioxus and Recoil.
//! This project is confined to a single file to showcase the suggested patterns
//! for building a small but mighty UI with Dioxus without worrying about project structure.
//!
//! If you want an example on recommended project structure, check out the TodoMVC folder
//!
//! Here, we show to use Dioxus' Recoil state management solution to simplify app logic
#![allow(non_snake_case)]
use dioxus_core::prelude::*;
use dioxus_web::WebsysRenderer;
use dioxus_web::dioxus::prelude::*;
use recoil::*;
use std::collections::HashMap;
use uuid::Uuid;
static APP_STYLE: &'static str = include_str!("./todomvc/style.css");
pub static TODOS: AtomFamily<uuid::Uuid, TodoItem> = atom_family(|_| {});
pub static FILTER: Atom<FilterState> = atom(|_| FilterState::All);
pub static SHOW_ALL_TODOS: selector<bool> = selector(|g| g.getter(|f| false));
const APP_STYLE: &'static str = include_str!("./todomvc/style.css");
fn main() {
wasm_bindgen_futures::spawn_local(WebsysRenderer::start(|ctx, props| {
ctx.render(rsx! {
div {
id: "app",
style { "{APP_STYLE}" }
TodoList {}
Footer {}
wasm_bindgen_futures::spawn_local(dioxus_web::WebsysRenderer::start(App));
}
// Declare our global app state
const TODO_LIST: Atom<HashMap<Uuid, TodoItem>> = atom(|_| Default::default());
const FILTER: Atom<FilterState> = atom(|_| FilterState::All);
const TODOS_LEFT: selector<usize> = selector(|api| api.get(&TODO_LIST).len());
// Implement a simple abstraction over sets/gets of multiple atoms
struct TodoManager(RecoilContext);
impl TodoManager {
fn add_todo(&self, contents: String) {
let item = TodoItem {
checked: false,
contents,
id: Uuid::new_v4(),
};
self.0.modify(&TODO_LIST, move |list| {
list.insert(item.id, item);
});
}
fn remove_todo(&self, id: &Uuid) {
self.0.modify(&TODO_LIST, move |list| {
list.remove(id);
})
}
fn select_all_todos(&self) {
self.0.modify(&TODO_LIST, move |list| {
for item in list.values_mut() {
item.checked = true;
}
})
}));
}
fn toggle_todo(&self, id: &Uuid) {
self.0.modify(&TODO_LIST, move |list| {
list.get_mut(id).map(|item| item.checked = !item.checked)
});
}
fn clear_completed(&self) {
self.0.modify(&TODO_LIST, move |list| {
*list = list.drain().filter(|(_, item)| !item.checked).collect();
})
}
fn set_filter(&self, filter: &FilterState) {
self.0.modify(&FILTER, move |f| *f = *filter);
}
}
#[derive(PartialEq, Clone, Copy)]
pub enum FilterState {
All,
Active,
Completed,
}
#[derive(Debug, PartialEq, Clone)]
pub struct TodoItem {
pub id: Uuid,
pub checked: bool,
pub contents: String,
}
fn App(ctx: Context, props: &()) -> DomTree {
init_recoil_root(ctx);
ctx.render(rsx! {
div { id: "app", style { "{APP_STYLE}" }
TodoList {}
Footer {}
}
})
}
pub fn TodoList(ctx: Context, props: &()) -> DomTree {
let (draft, set_draft) = use_state(&ctx, || "".to_string());
let (todos, _) = use_state(&ctx, || Vec::<TodoItem>::new());
let draft = use_state_new(&ctx, || "".to_string());
let todos = use_atom(&ctx, &TODO_LIST);
let filter = use_atom(&ctx, &FILTER);
let todolist = todos
.values()
.filter(|item| match filter {
FilterState::All => true,
FilterState::Active => !item.checked,
FilterState::Completed => item.checked,
})
.map(|item| {
rsx!(TodoEntry {
key: "{order}",
id: item.id,
})
});
ctx.render(rsx! {
div {
header {
@@ -35,43 +118,26 @@ pub fn TodoList(ctx: Context, props: &()) -> DomTree {
class: "new-todo"
placeholder: "What needs to be done?"
value: "{draft}"
oninput: move |evt| set_draft(evt.value)
oninput: move |evt| draft.set(evt.value)
}
}
{todolist}
{ // list
todos
.iter()
.filter(|item| match filter {
FilterState::All => true,
FilterState::Active => !item.checked,
FilterState::Completed => item.checked,
})
.map(|item| {
rsx!(TodoEntry {
key: "{order}",
id: item.id,
})
})
}
// filter toggle (show only if the list isn't empty)
{(!todos.is_empty()).then(||
rsx!( FilterToggles {})
)}
// rsx! accepts optionals, so we suggest the `then` method in place of ternary
{(!todos.is_empty()).then(|| rsx!( FilterToggles {}) )}
}
})
}
#[derive(PartialEq, Props)]
pub struct TodoEntryProps {
id: uuid::Uuid,
id: Uuid,
}
pub fn TodoEntry(ctx: Context, props: &TodoEntryProps) -> DomTree {
let (is_editing, set_is_editing) = use_state(&ctx, || false);
let todo = use_atom_family(&ctx, &TODOS, props.id);
let contents = "";
let todo = use_atom(&ctx, &TODO_LIST).get(&props.id).unwrap();
// let todo = use_atom_family(&ctx, &TODOS, props.id);
ctx.render(rsx! (
li {
@@ -83,7 +149,7 @@ pub fn TodoEntry(ctx: Context, props: &TodoEntryProps) -> DomTree {
}
{is_editing.then(|| rsx!(
input {
value: "{contents}"
value: "{todo.contents}"
}
))}
}
@@ -91,8 +157,8 @@ pub fn TodoEntry(ctx: Context, props: &TodoEntryProps) -> DomTree {
}
pub fn FilterToggles(ctx: Context, props: &()) -> DomTree {
let reducer = recoil::use_callback(&ctx, || ());
let items_left = recoil::use_atom_family(&ctx, &TODOS, uuid::Uuid::new_v4());
let reducer = use_recoil_context::<TodoManager>(ctx);
let items_left = use_selector(ctx, &TODOS_LEFT);
let toggles = [
("All", "", FilterState::All),
@@ -105,19 +171,20 @@ pub fn FilterToggles(ctx: Context, props: &()) -> DomTree {
li {
class: "{name}"
a {
href: "{path}"
onclick: move |_| reducer.set_filter(&filter)
href: "{path}",
onclick: move |_| reducer.set_filter(&filter),
"{name}"
}
}
)
});
// todo
let item_text = "";
let items_left = "";
let item_text = match items_left {
1 => "item",
_ => "items",
};
ctx.render(rsx! {
rsx! { in ctx,
footer {
span {
strong {"{items_left}"}
@@ -128,11 +195,11 @@ pub fn FilterToggles(ctx: Context, props: &()) -> DomTree {
{toggles}
}
}
})
}
}
pub fn Footer(ctx: Context, props: &()) -> DomTree {
ctx.render(rsx! {
rsx!( in ctx,
footer {
class: "info"
p {"Double-click to edit a todo"}
@@ -145,134 +212,5 @@ pub fn Footer(ctx: Context, props: &()) -> DomTree {
a { "TodoMVC", href: "http://todomvc.com" }
}
}
})
}
#[derive(PartialEq)]
pub enum FilterState {
All,
Active,
Completed,
}
#[derive(Debug, PartialEq, Clone)]
pub struct TodoItem {
pub id: uuid::Uuid,
pub checked: bool,
pub contents: String,
}
impl RecoilContext<()> {
pub fn add_todo(&self, contents: String) {}
pub fn remove_todo(&self, id: &uuid::Uuid) {}
pub fn select_all_todos(&self) {}
pub fn toggle_todo(&self, id: &uuid::Uuid) {}
pub fn clear_completed(&self) {}
pub fn set_filter(&self, filter: &FilterState) {}
}
pub use recoil::*;
mod recoil {
use dioxus_core::context::Context;
pub struct RecoilContext<T: 'static> {
_inner: T,
}
impl<T: 'static> RecoilContext<T> {
/// Get the value of an atom. Returns a reference to the underlying data.
pub fn get(&self) {}
/// Replace an existing value with a new value
///
/// This does not replace the value instantly, and all calls to "get" within the current scope will return
pub fn set(&self) {}
// Modify lets you modify the value in place. However, because there's no previous value around to compare
// the new one with, we are unable to memoize the change. As such, all downsteam users of this Atom will
// be updated, causing all subsrcibed components to re-render.
//
// This is fine for most values, but might not be performant when dealing with collections. For collections,
// use the "Family" variants as these will stay memoized for inserts, removals, and modifications.
//
// Note - like "set" this won't propogate instantly. Once all "gets" are dropped, only then will we run the
pub fn modify(&self) {}
}
pub fn use_callback<'a, G>(c: &Context<'a>, f: impl Fn() -> G) -> &'a RecoilContext<G> {
todo!()
}
pub fn use_atom<T: PartialEq, O>(c: &Context, t: &'static Atom<T>) -> O {
todo!()
}
pub fn use_batom<T: PartialEq, O>(c: &Context, t: impl Readable) -> O {
todo!()
}
pub trait Readable {}
impl<T: PartialEq> Readable for &'static Atom<T> {}
impl<K: PartialEq, V: PartialEq> Readable for &'static AtomFamily<K, V> {}
pub fn use_atom_family<'a, K: PartialEq, V: PartialEq>(
c: &Context<'a>,
t: &'static AtomFamily<K, V>,
g: K,
) -> &'a V {
todo!()
}
pub use atoms::{atom, Atom};
pub use atoms::{atom_family, AtomFamily};
mod atoms {
use super::*;
pub struct AtomBuilder<T: PartialEq> {
pub key: String,
pub manual_init: Option<Box<dyn Fn() -> T>>,
_never: std::marker::PhantomData<T>,
}
impl<T: PartialEq> AtomBuilder<T> {
pub fn new() -> Self {
Self {
key: uuid::Uuid::new_v4().to_string(),
manual_init: None,
_never: std::marker::PhantomData {},
}
}
pub fn init<A: Fn() -> T + 'static>(&mut self, f: A) {
self.manual_init = Some(Box::new(f));
}
pub fn set_key(&mut self, _key: &'static str) {}
}
pub struct atom<T: PartialEq>(pub fn(&mut AtomBuilder<T>) -> T);
pub type Atom<T: PartialEq> = atom<T>;
pub struct AtomFamilyBuilder<K, V> {
_never: std::marker::PhantomData<(K, V)>,
}
pub struct atom_family<K: PartialEq, V: PartialEq>(pub fn(&mut AtomFamilyBuilder<K, V>));
pub type AtomFamily<K: PartialEq, V: PartialEq> = atom_family<K, V>;
}
pub use selectors::selector;
mod selectors {
pub struct SelectorBuilder<Out, const Built: bool> {
_p: std::marker::PhantomData<Out>,
}
impl<O> SelectorBuilder<O, false> {
pub fn getter(self, f: impl Fn(()) -> O) -> SelectorBuilder<O, true> {
todo!()
// std::rc::Rc::pin(value)
// todo!()
}
}
pub struct selector<O>(pub fn(SelectorBuilder<O, false>) -> SelectorBuilder<O, true>);
}
)
}
+1 -1
View File
@@ -100,7 +100,7 @@ impl WebsysRenderer {
log::debug!("Received edits: {:#?}", edits);
for edit in &edits {
log::debug!("edit stream {:?}", edit);
// log::debug!("edit stream {:?}", edit);
// log::debug!("Stream stack {:#?}", patch_machine.stack.top());
patch_machine.handle_edit(edit);
}