Skip to content

Commit

Permalink
WIP: local state
Browse files Browse the repository at this point in the history
  • Loading branch information
kmicklas committed Jun 14, 2024
1 parent 7cc7a3e commit 588cf4c
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 2 deletions.
24 changes: 23 additions & 1 deletion examples/tutorial/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::collections::BTreeMap;

use ravel::with;
use ravel::{with, with_state};
use ravel_web::{
any, attr,
collections::{btree_map, slice},
Expand Down Expand Up @@ -112,6 +112,27 @@ fn events() -> View!(Model) {
)
}

fn local_state() -> View!(Model) {
with_state(
|| 0,
|cx, local_count| {
cx.build((
el::h2("Local state"),
el::p(("Local count: ", display(*local_count.borrow()))),
el::p(el::button((
"Increment local count",
on_(event::Click, {
let local_count = local_count.clone();
move |_| {
*local_count.borrow_mut() += 1;
}
}),
))),
))
},
)
}

/// All of our views so far have had a static structure. Sometimes, we need to
/// swap out or hide various components.
fn dynamic_view(model: &Model) -> View!(Model, '_) {
Expand Down Expand Up @@ -198,6 +219,7 @@ fn tutorial(model: &Model) -> View!(Model, '_) {
basic_html(),
state(model),
events(),
local_state(),
dynamic_view(model),
lists(model),
)
Expand Down
13 changes: 12 additions & 1 deletion ravel-web/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use std::sync::Arc;

use atomic_waker::AtomicWaker;
use dom::Position;
use ravel::{Builder, Cx, CxRep};
use ravel::{Builder, Cx, CxRep, WithStateState};

mod any;
pub mod attr;
Expand Down Expand Up @@ -50,9 +50,20 @@ pub trait State<Output>: AsAny {
fn run(&mut self, output: &mut Output);
}

impl<Output, T: 'static, S> State<Output> for WithStateState<T, S>
where
S: State<Output>,
{
fn run(&mut self, output: &mut Output) {
self.inner.run(output)
}
}

/// A marker trait for the [`State`] types of a [`trait@View`].
pub trait ViewMarker {}

impl<T: 'static, S: ViewMarker> ViewMarker for WithStateState<T, S> {}

macro_rules! tuple_state {
($($a:ident),*) => {
#[allow(non_camel_case_types)]
Expand Down
4 changes: 4 additions & 0 deletions ravel/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ use std::{marker::PhantomData, mem::MaybeUninit};

use paste::paste;

mod state;

pub use state::*;

/// A dummy type which typically represents a "backend".
pub trait CxRep {
type BuildCx<'a>: Copy;
Expand Down
47 changes: 47 additions & 0 deletions ravel/src/state.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
use std::{cell::RefCell, marker::PhantomData, rc::Rc};

use crate::{with, Builder, Cx, CxRep, Token};

pub struct WithState<Init, Build, S> {
init: Init,
build: Build,
phantom: PhantomData<S>,
}

impl<R: CxRep, T, Init, Build, S> Builder<R> for WithState<Init, Build, S>
where
Init: FnOnce() -> T,
Build: FnOnce(Cx<S, R>, &Rc<RefCell<T>>) -> Token<S>,
{
type State = WithStateState<T, S>;

fn build(self, cx: R::BuildCx<'_>) -> Self::State {
let value = Rc::new(RefCell::new((self.init)()));
let inner = with(|cx| (self.build)(cx, &value)).build(cx);
WithStateState { value, inner }
}

fn rebuild(self, cx: R::RebuildCx<'_>, state: &mut Self::State) {
with(|cx| (self.build)(cx, &state.value)).rebuild(cx, &mut state.inner)
}
}

pub struct WithStateState<T, S> {
value: Rc<RefCell<T>>,
pub inner: S,
}

pub fn with_state<T, Init, Build, S, R: CxRep>(
init: Init,
build: Build,
) -> WithState<Init, Build, S>
where
Init: FnOnce() -> T,
Build: FnOnce(Cx<S, R>, &Rc<RefCell<T>>) -> Token<S>,
{
WithState {
init,
build,
phantom: PhantomData,
}
}

0 comments on commit 588cf4c

Please sign in to comment.