From efd060c95502efcc1428a45ea93bee48aad11c13 Mon Sep 17 00:00:00 2001 From: Greg Johnston Date: Fri, 12 Jul 2024 11:07:07 -0400 Subject: [PATCH] feat: Suspend on style: and class: --- tachys/src/reactive_graph/class.rs | 86 ++++++++++++++++++++++++++- tachys/src/reactive_graph/style.rs | 37 ++++++++++-- tachys/src/reactive_graph/suspense.rs | 2 +- 3 files changed, 116 insertions(+), 9 deletions(-) diff --git a/tachys/src/reactive_graph/class.rs b/tachys/src/reactive_graph/class.rs index cf1163006..868997607 100644 --- a/tachys/src/reactive_graph/class.rs +++ b/tachys/src/reactive_graph/class.rs @@ -1,9 +1,10 @@ -use super::{ReactiveFunction, SharedReactiveFunction}; +use super::{ReactiveFunction, SharedReactiveFunction, Suspend}; use crate::{html::class::IntoClass, renderer::DomRenderer}; +use any_spawner::Executor; +use futures::FutureExt; use reactive_graph::{effect::RenderEffect, signal::guards::ReadGuard}; use std::{ - borrow::{Borrow, Cow}, - ops::Deref, + borrow::Borrow, cell::RefCell, future::Future, ops::Deref, rc::Rc, sync::Arc, }; @@ -692,3 +693,82 @@ mod stable { class_signal!(ArcMemo); class_signal!(ArcSignal); } + +impl IntoClass for Suspend +where + Fut: Clone + Future + Send + 'static, + Fut::Output: IntoClass, + Rndr: DomRenderer + 'static, +{ + type AsyncOutput = Fut::Output; + type State = Rc>::State>>>; + type Cloneable = Self; + type CloneableOwned = Self; + + fn html_len(&self) -> usize { + 0 + } + + fn to_html(self, style: &mut String) { + if let Some(inner) = self.0.now_or_never() { + inner.to_html(style); + } else { + panic!("You cannot use Suspend on an attribute outside Suspense"); + } + } + + fn hydrate( + self, + el: &::Element, + ) -> Self::State { + let el = el.to_owned(); + let state = Rc::new(RefCell::new(None)); + Executor::spawn_local({ + let state = Rc::clone(&state); + async move { + *state.borrow_mut() = + Some(self.0.await.hydrate::(&el)); + } + }); + state + } + + fn build(self, el: &::Element) -> Self::State { + let el = el.to_owned(); + let state = Rc::new(RefCell::new(None)); + Executor::spawn_local({ + let state = Rc::clone(&state); + async move { + *state.borrow_mut() = Some(self.0.await.build(&el)); + } + }); + state + } + + fn rebuild(self, state: &mut Self::State) { + Executor::spawn_local({ + let state = Rc::clone(&state); + async move { + let value = self.0.await; + let mut state = state.borrow_mut(); + if let Some(state) = state.as_mut() { + value.rebuild(state); + } + } + }); + } + + fn into_cloneable(self) -> Self::Cloneable { + self + } + + fn into_cloneable_owned(self) -> Self::CloneableOwned { + self + } + + fn dry_resolve(&mut self) {} + + async fn resolve(self) -> Self::AsyncOutput { + self.0.await + } +} diff --git a/tachys/src/reactive_graph/style.rs b/tachys/src/reactive_graph/style.rs index 0e2eb0243..25bbd18c9 100644 --- a/tachys/src/reactive_graph/style.rs +++ b/tachys/src/reactive_graph/style.rs @@ -1,8 +1,9 @@ use super::{ReactiveFunction, SharedReactiveFunction, Suspend}; use crate::{html::style::IntoStyle, renderer::DomRenderer}; +use any_spawner::Executor; use futures::FutureExt; use reactive_graph::effect::RenderEffect; -use std::{borrow::Cow, future::Future}; +use std::{borrow::Cow, cell::RefCell, future::Future, rc::Rc}; impl IntoStyle for (&'static str, F) where @@ -432,7 +433,7 @@ where Rndr: DomRenderer + 'static, { type AsyncOutput = Fut::Output; - type State = (); + type State = Rc>::State>>>; type Cloneable = Self; type CloneableOwned = Self; @@ -448,15 +449,41 @@ where self, el: &::Element, ) -> Self::State { - todo!() + let el = el.to_owned(); + let state = Rc::new(RefCell::new(None)); + Executor::spawn_local({ + let state = Rc::clone(&state); + async move { + *state.borrow_mut() = + Some(self.0.await.hydrate::(&el)); + } + }); + state } fn build(self, el: &::Element) -> Self::State { - todo!() + let el = el.to_owned(); + let state = Rc::new(RefCell::new(None)); + Executor::spawn_local({ + let state = Rc::clone(&state); + async move { + *state.borrow_mut() = Some(self.0.await.build(&el)); + } + }); + state } fn rebuild(self, state: &mut Self::State) { - todo!() + Executor::spawn_local({ + let state = Rc::clone(&state); + async move { + let value = self.0.await; + let mut state = state.borrow_mut(); + if let Some(state) = state.as_mut() { + value.rebuild(state); + } + } + }); } fn into_cloneable(self) -> Self::Cloneable { diff --git a/tachys/src/reactive_graph/suspense.rs b/tachys/src/reactive_graph/suspense.rs index 455a7a5cf..793573602 100644 --- a/tachys/src/reactive_graph/suspense.rs +++ b/tachys/src/reactive_graph/suspense.rs @@ -1,5 +1,5 @@ use crate::{ - html::{attribute::Attribute, style::IntoStyle}, + html::attribute::Attribute, hydration::Cursor, renderer::Renderer, ssr::StreamBuilder,