implement rendering traits for signals directly on stable
This commit is contained in:
parent
439deea066
commit
e0e67360aa
|
@ -1,7 +1,7 @@
|
|||
use leptos::{
|
||||
effect::Effect,
|
||||
leptos_dom::helpers::{set_interval_with_handle, IntervalHandle},
|
||||
prelude::*,
|
||||
reactive_graph::effect::Effect,
|
||||
signals::RwSignal,
|
||||
*,
|
||||
};
|
||||
|
@ -30,10 +30,9 @@ pub fn TimerDemo() -> impl IntoView {
|
|||
<div>{count_a}</div>
|
||||
<div>"Count B (dynamic interval, currently " {interval} " ms)"</div>
|
||||
<div>{count_b}</div>
|
||||
// TODO impl Property directly on signal types in stable
|
||||
<input prop:value=interval on:input:target=move |ev| {
|
||||
if let Ok(value) = ev.target().value().parse::<u64>() {
|
||||
interval.set.set(value);
|
||||
if let Ok(value) = ev.target().value().parse::<i32>() {
|
||||
interval.set(value);
|
||||
}
|
||||
}/>
|
||||
</div>
|
||||
|
|
|
@ -16,7 +16,7 @@ use std::{
|
|||
sync::{Arc, RwLock},
|
||||
};
|
||||
|
||||
pub struct RwSignal<T: Send + Sync + 'static> {
|
||||
pub struct RwSignal<T: 'static> {
|
||||
#[cfg(debug_assertions)]
|
||||
defined_at: &'static Location<'static>,
|
||||
inner: StoredValue<ArcRwSignal<T>>,
|
||||
|
@ -90,15 +90,15 @@ impl<T: Send + Sync + 'static> RwSignal<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: Send + Sync + 'static> Copy for RwSignal<T> {}
|
||||
impl<T: 'static> Copy for RwSignal<T> {}
|
||||
|
||||
impl<T: Send + Sync + 'static> Clone for RwSignal<T> {
|
||||
impl<T: 'static> Clone for RwSignal<T> {
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Send + Sync + 'static> Debug for RwSignal<T> {
|
||||
impl<T: 'static> Debug for RwSignal<T> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("RwSignal")
|
||||
.field("type", &std::any::type_name::<T>())
|
||||
|
@ -107,21 +107,21 @@ impl<T: Send + Sync + 'static> Debug for RwSignal<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: Send + Sync + 'static> PartialEq for RwSignal<T> {
|
||||
impl<T: 'static> PartialEq for RwSignal<T> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.inner == other.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Send + Sync + 'static> Eq for RwSignal<T> {}
|
||||
impl<T: 'static> Eq for RwSignal<T> {}
|
||||
|
||||
impl<T: Send + Sync + 'static> Hash for RwSignal<T> {
|
||||
impl<T: 'static> Hash for RwSignal<T> {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
self.inner.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Send + Sync + 'static> DefinedAt for RwSignal<T> {
|
||||
impl<T: 'static> DefinedAt for RwSignal<T> {
|
||||
fn defined_at(&self) -> Option<&'static Location<'static>> {
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
|
|
|
@ -144,33 +144,7 @@ where
|
|||
}
|
||||
|
||||
fn rebuild(self, _state: &mut Self::State) {
|
||||
// TODO — knowing how and whether to rebuild effects like this is tricky
|
||||
// it's the one place I've run into "stale values" when experimenting with this model
|
||||
|
||||
/* let (name, mut f) = self;
|
||||
let prev_effect = std::mem::take(&mut state.0);
|
||||
let prev_value = prev_effect.as_ref().and_then(|e| e.take_value());
|
||||
drop(prev_effect);
|
||||
*state = RenderEffect::new_with_value(
|
||||
move |prev| {
|
||||
let include = f();
|
||||
match prev {
|
||||
Some((class_list, prev)) => {
|
||||
if include {
|
||||
if !prev {
|
||||
R::add_class(&class_list, name);
|
||||
}
|
||||
} else if prev {
|
||||
R::remove_class(&class_list, name);
|
||||
}
|
||||
(class_list.clone(), include)
|
||||
}
|
||||
None => unreachable!(),
|
||||
}
|
||||
},
|
||||
prev_value,
|
||||
)
|
||||
.into(); */
|
||||
// TODO rebuild?
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -250,3 +224,172 @@ where
|
|||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "nightly"))]
|
||||
mod stable {
|
||||
macro_rules! class_signal {
|
||||
($sig:ident) => {
|
||||
impl<C, R> IntoClass<R> for $sig<C>
|
||||
where
|
||||
C: IntoClass<R> + Clone + Send + Sync + 'static,
|
||||
C::State: 'static,
|
||||
R: DomRenderer,
|
||||
{
|
||||
type State = RenderEffectState<C::State>;
|
||||
|
||||
fn html_len(&self) -> usize {
|
||||
0
|
||||
}
|
||||
|
||||
fn to_html(self, class: &mut String) {
|
||||
let value = self.get();
|
||||
value.to_html(class);
|
||||
}
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
el: &R::Element,
|
||||
) -> Self::State {
|
||||
(move || self.get()).hydrate::<FROM_SERVER>(el)
|
||||
}
|
||||
|
||||
fn build(self, el: &R::Element) -> Self::State {
|
||||
(move || self.get()).build(el)
|
||||
}
|
||||
|
||||
fn rebuild(self, _state: &mut Self::State) {
|
||||
// TODO rebuild here?
|
||||
}
|
||||
}
|
||||
|
||||
impl<R> IntoClass<R> for (&'static str, $sig<bool>)
|
||||
where
|
||||
R: DomRenderer,
|
||||
{
|
||||
type State = RenderEffectState<(R::ClassList, bool)>;
|
||||
|
||||
fn html_len(&self) -> usize {
|
||||
self.0.len()
|
||||
}
|
||||
|
||||
fn to_html(self, class: &mut String) {
|
||||
let (name, f) = self;
|
||||
let include = f.get();
|
||||
if include {
|
||||
<&str as IntoClass<R>>::to_html(name, class);
|
||||
}
|
||||
}
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
el: &R::Element,
|
||||
) -> Self::State {
|
||||
IntoClass::<R>::hydrate::<FROM_SERVER>(
|
||||
(self.0, move || self.1.get()),
|
||||
el,
|
||||
)
|
||||
}
|
||||
|
||||
fn build(self, el: &R::Element) -> Self::State {
|
||||
IntoClass::<R>::build((self.0, move || self.1.get()), el)
|
||||
}
|
||||
|
||||
fn rebuild(self, _state: &mut Self::State) {
|
||||
// TODO rebuild here?
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! class_signal_unsend {
|
||||
($sig:ident) => {
|
||||
impl<C, R> IntoClass<R> for $sig<C>
|
||||
where
|
||||
C: IntoClass<R> + Clone + 'static,
|
||||
C::State: 'static,
|
||||
R: DomRenderer,
|
||||
{
|
||||
type State = RenderEffectState<C::State>;
|
||||
|
||||
fn html_len(&self) -> usize {
|
||||
0
|
||||
}
|
||||
|
||||
fn to_html(self, class: &mut String) {
|
||||
let value = self.get();
|
||||
value.to_html(class);
|
||||
}
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
el: &R::Element,
|
||||
) -> Self::State {
|
||||
(move || self.get()).hydrate::<FROM_SERVER>(el)
|
||||
}
|
||||
|
||||
fn build(self, el: &R::Element) -> Self::State {
|
||||
(move || self.get()).build(el)
|
||||
}
|
||||
|
||||
fn rebuild(self, _state: &mut Self::State) {
|
||||
// TODO rebuild here?
|
||||
}
|
||||
}
|
||||
|
||||
impl<R> IntoClass<R> for (&'static str, $sig<bool>)
|
||||
where
|
||||
R: DomRenderer,
|
||||
{
|
||||
type State = RenderEffectState<(R::ClassList, bool)>;
|
||||
|
||||
fn html_len(&self) -> usize {
|
||||
self.0.len()
|
||||
}
|
||||
|
||||
fn to_html(self, class: &mut String) {
|
||||
let (name, f) = self;
|
||||
let include = f.get();
|
||||
if include {
|
||||
<&str as IntoClass<R>>::to_html(name, class);
|
||||
}
|
||||
}
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
el: &R::Element,
|
||||
) -> Self::State {
|
||||
IntoClass::<R>::hydrate::<FROM_SERVER>(
|
||||
(self.0, move || self.1.get()),
|
||||
el,
|
||||
)
|
||||
}
|
||||
|
||||
fn build(self, el: &R::Element) -> Self::State {
|
||||
IntoClass::<R>::build((self.0, move || self.1.get()), el)
|
||||
}
|
||||
|
||||
fn rebuild(self, _state: &mut Self::State) {
|
||||
// TODO rebuild here?
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
use super::RenderEffectState;
|
||||
use crate::{html::class::IntoClass, renderer::DomRenderer};
|
||||
use reactive_graph::{
|
||||
computed::{ArcMemo, Memo},
|
||||
signal::{ArcReadSignal, ArcRwSignal, ReadSignal, RwSignal},
|
||||
traits::Get,
|
||||
wrappers::read::{ArcSignal, Signal},
|
||||
};
|
||||
|
||||
class_signal!(RwSignal);
|
||||
class_signal!(ReadSignal);
|
||||
class_signal!(Memo);
|
||||
class_signal!(Signal);
|
||||
class_signal_unsend!(ArcRwSignal);
|
||||
class_signal_unsend!(ArcReadSignal);
|
||||
class_signal!(ArcMemo);
|
||||
class_signal!(ArcSignal);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,155 @@
|
|||
use crate::{
|
||||
html::element::InnerHtmlValue,
|
||||
renderer::{DomRenderer, Renderer},
|
||||
};
|
||||
use reactive_graph::effect::RenderEffect;
|
||||
|
||||
impl<F, V, R> InnerHtmlValue<R> for F
|
||||
where
|
||||
F: FnMut() -> V + 'static,
|
||||
V: InnerHtmlValue<R>,
|
||||
V::State: 'static,
|
||||
R: DomRenderer,
|
||||
{
|
||||
type State = RenderEffect<V::State>;
|
||||
|
||||
fn html_len(&self) -> usize {
|
||||
0
|
||||
}
|
||||
|
||||
fn to_html(mut self, buf: &mut String) {
|
||||
let value = self();
|
||||
value.to_html(buf);
|
||||
}
|
||||
|
||||
fn to_template(_buf: &mut String) {}
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
mut self,
|
||||
el: &<R as Renderer>::Element,
|
||||
) -> Self::State {
|
||||
let el = el.to_owned();
|
||||
RenderEffect::new(move |prev| {
|
||||
let value = self();
|
||||
if let Some(mut state) = prev {
|
||||
value.rebuild(&mut state);
|
||||
state
|
||||
} else {
|
||||
value.hydrate::<FROM_SERVER>(&el)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn build(mut self, el: &<R as Renderer>::Element) -> Self::State {
|
||||
let el = el.to_owned();
|
||||
RenderEffect::new(move |prev| {
|
||||
let value = self();
|
||||
if let Some(mut state) = prev {
|
||||
value.rebuild(&mut state);
|
||||
state
|
||||
} else {
|
||||
value.build(&el)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn rebuild(self, _state: &mut Self::State) {}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "nightly"))]
|
||||
mod stable {
|
||||
use crate::{
|
||||
html::element::InnerHtmlValue,
|
||||
renderer::{DomRenderer, Renderer},
|
||||
};
|
||||
use reactive_graph::{
|
||||
computed::{ArcMemo, Memo},
|
||||
effect::RenderEffect,
|
||||
signal::{ArcReadSignal, ArcRwSignal, ReadSignal, RwSignal},
|
||||
traits::Get,
|
||||
wrappers::read::{ArcSignal, Signal},
|
||||
};
|
||||
|
||||
macro_rules! inner_html_signal {
|
||||
($sig:ident) => {
|
||||
impl<V, R> InnerHtmlValue<R> for $sig<V>
|
||||
where
|
||||
V: InnerHtmlValue<R> + Send + Sync + Clone + 'static,
|
||||
V::State: 'static,
|
||||
R: DomRenderer,
|
||||
{
|
||||
type State = RenderEffect<V::State>;
|
||||
|
||||
fn html_len(&self) -> usize {
|
||||
0
|
||||
}
|
||||
|
||||
fn to_html(self, buf: &mut String) {
|
||||
let value = self.get();
|
||||
value.to_html(buf);
|
||||
}
|
||||
|
||||
fn to_template(_buf: &mut String) {}
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
el: &<R as Renderer>::Element,
|
||||
) -> Self::State {
|
||||
(move || self.get()).hydrate::<FROM_SERVER>(el)
|
||||
}
|
||||
|
||||
fn build(self, el: &<R as Renderer>::Element) -> Self::State {
|
||||
(move || self.get()).build(el)
|
||||
}
|
||||
|
||||
fn rebuild(self, _state: &mut Self::State) {}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! inner_html_signal_unsend {
|
||||
($sig:ident) => {
|
||||
impl<V, R> InnerHtmlValue<R> for $sig<V>
|
||||
where
|
||||
V: InnerHtmlValue<R> + Clone + 'static,
|
||||
V::State: 'static,
|
||||
R: DomRenderer,
|
||||
{
|
||||
type State = RenderEffect<V::State>;
|
||||
|
||||
fn html_len(&self) -> usize {
|
||||
0
|
||||
}
|
||||
|
||||
fn to_html(self, buf: &mut String) {
|
||||
let value = self.get();
|
||||
value.to_html(buf);
|
||||
}
|
||||
|
||||
fn to_template(_buf: &mut String) {}
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
el: &<R as Renderer>::Element,
|
||||
) -> Self::State {
|
||||
(move || self.get()).hydrate::<FROM_SERVER>(el)
|
||||
}
|
||||
|
||||
fn build(self, el: &<R as Renderer>::Element) -> Self::State {
|
||||
(move || self.get()).build(el)
|
||||
}
|
||||
|
||||
fn rebuild(self, _state: &mut Self::State) {}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
inner_html_signal!(RwSignal);
|
||||
inner_html_signal!(ReadSignal);
|
||||
inner_html_signal!(Memo);
|
||||
inner_html_signal!(Signal);
|
||||
inner_html_signal_unsend!(ArcRwSignal);
|
||||
inner_html_signal_unsend!(ArcReadSignal);
|
||||
inner_html_signal!(ArcMemo);
|
||||
inner_html_signal!(ArcSignal);
|
||||
}
|
|
@ -1,12 +1,8 @@
|
|||
use crate::{
|
||||
async_views::Suspend,
|
||||
html::{
|
||||
attribute::{Attribute, AttributeValue},
|
||||
element::InnerHtmlValue,
|
||||
property::IntoProperty,
|
||||
},
|
||||
html::attribute::{Attribute, AttributeValue},
|
||||
hydration::Cursor,
|
||||
renderer::{DomRenderer, Renderer},
|
||||
renderer::Renderer,
|
||||
ssr::StreamBuilder,
|
||||
view::{
|
||||
add_attr::AddAnyAttr, Mountable, Position, PositionState, Render,
|
||||
|
@ -22,8 +18,10 @@ use reactive_graph::{
|
|||
|
||||
mod class;
|
||||
mod guards;
|
||||
mod inner_html;
|
||||
pub mod node_ref;
|
||||
mod owned;
|
||||
mod property;
|
||||
mod style;
|
||||
pub use owned::*;
|
||||
|
||||
|
@ -113,25 +111,7 @@ where
|
|||
|
||||
#[track_caller]
|
||||
fn rebuild(self, _state: &mut Self::State) {
|
||||
// TODO — knowing how and whether to rebuild effects like this is tricky
|
||||
// it's the one place I've run into "stale values" when experimenting with this model
|
||||
|
||||
/* let prev_effect = mem::take(&mut state.0);
|
||||
let prev_value = prev_effect.as_ref().and_then(|e| e.take_value());
|
||||
drop(prev_effect);
|
||||
*state = RenderEffect::new_with_value(
|
||||
move |prev| {
|
||||
let value = self();
|
||||
if let Some(mut state) = prev {
|
||||
value.rebuild(&mut state);
|
||||
state
|
||||
} else {
|
||||
value.build()
|
||||
}
|
||||
},
|
||||
prev_value,
|
||||
)
|
||||
.into(); */
|
||||
// TODO rebuild
|
||||
}
|
||||
|
||||
fn try_rebuild(
|
||||
|
@ -374,17 +354,6 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
// Extends to track suspense
|
||||
impl<const TRANSITION: bool, Fal, Fut> Suspend<TRANSITION, Fal, Fut> {
|
||||
pub fn track(self) -> Suspend<TRANSITION, Fal, ScopedFuture<Fut>> {
|
||||
let Suspend { fallback, fut } = self;
|
||||
Suspend {
|
||||
fallback,
|
||||
fut: ScopedFuture::new(fut),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Dynamic attributes
|
||||
impl<F, V, R> AttributeValue<R> for F
|
||||
where
|
||||
|
@ -449,184 +418,290 @@ where
|
|||
}
|
||||
|
||||
fn rebuild(self, _key: &str, _state: &mut Self::State) {
|
||||
// TODO — knowing how and whether to rebuild effects like this is tricky
|
||||
// it's the one place I've run into "stale values" when experimenting with this model
|
||||
|
||||
// TODO
|
||||
/* let prev_effect = mem::take(&mut state.0);
|
||||
let prev_value = prev_effect.as_ref().and_then(|e| e.take_value());
|
||||
drop(prev_effect);
|
||||
let key = key.to_owned();
|
||||
*state = RenderEffect::new_with_value(
|
||||
move |prev| {
|
||||
crate::log(&format!(
|
||||
"inside here, prev is some? {}",
|
||||
prev.is_some()
|
||||
));
|
||||
let value = self();
|
||||
if let Some(mut state) = prev {
|
||||
value.rebuild(&key, &mut state);
|
||||
state
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
},
|
||||
prev_value,
|
||||
)
|
||||
.into(); */
|
||||
}
|
||||
|
||||
/* fn build(self) -> Self::State {
|
||||
RenderEffect::new(move |prev| {
|
||||
let value = self();
|
||||
if let Some(mut state) = prev {
|
||||
value.rebuild(&mut state);
|
||||
state
|
||||
} else {
|
||||
value.build()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn rebuild(self, state: &mut Self::State) {
|
||||
/* crate::log(&format!(
|
||||
"[REBUILDING EFFECT] Is this a mistake? {}",
|
||||
std::panic::Location::caller(),
|
||||
)); */
|
||||
let old_effect = std::mem::replace(state, self.build());
|
||||
} */
|
||||
}
|
||||
|
||||
// Dynamic properties
|
||||
// These do update during hydration because properties don't exist in the DOM
|
||||
impl<F, V, R> IntoProperty<R> for F
|
||||
where
|
||||
F: FnMut() -> V + 'static,
|
||||
V: IntoProperty<R>,
|
||||
V::State: 'static,
|
||||
R: DomRenderer,
|
||||
{
|
||||
type State = RenderEffectState<V::State>;
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
mut self,
|
||||
el: &<R as Renderer>::Element,
|
||||
key: &str,
|
||||
) -> Self::State {
|
||||
let key = R::intern(key);
|
||||
let key = key.to_owned();
|
||||
let el = el.to_owned();
|
||||
|
||||
RenderEffect::new(move |prev| {
|
||||
let value = self();
|
||||
if let Some(mut state) = prev {
|
||||
value.rebuild(&mut state, &key);
|
||||
state
|
||||
} else {
|
||||
value.hydrate::<FROM_SERVER>(&el, &key)
|
||||
}
|
||||
})
|
||||
.into()
|
||||
}
|
||||
|
||||
fn build(
|
||||
mut self,
|
||||
el: &<R as Renderer>::Element,
|
||||
key: &str,
|
||||
) -> Self::State {
|
||||
let key = R::intern(key);
|
||||
let key = key.to_owned();
|
||||
let el = el.to_owned();
|
||||
|
||||
RenderEffect::new(move |prev| {
|
||||
let value = self();
|
||||
if let Some(mut state) = prev {
|
||||
value.rebuild(&mut state, &key);
|
||||
state
|
||||
} else {
|
||||
value.build(&el, &key)
|
||||
}
|
||||
})
|
||||
.into()
|
||||
}
|
||||
|
||||
fn rebuild(self, _state: &mut Self::State, _key: &str) {
|
||||
// TODO — knowing how and whether to rebuild effects like this is tricky
|
||||
// it's the one place I've run into "stale values" when experimenting with this model
|
||||
|
||||
/* let prev_effect = mem::take(&mut state.0);
|
||||
let prev_value = prev_effect.as_ref().and_then(|e| e.take_value());
|
||||
drop(prev_effect);
|
||||
let key = key.to_owned();
|
||||
*state = RenderEffect::new_with_value(
|
||||
move |prev| {
|
||||
let value = self();
|
||||
if let Some(mut state) = prev {
|
||||
value.rebuild(&mut state, &key);
|
||||
state
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
},
|
||||
prev_value,
|
||||
)
|
||||
.into(); */
|
||||
// TODO rebuild
|
||||
}
|
||||
}
|
||||
|
||||
impl<F, V, R> InnerHtmlValue<R> for F
|
||||
where
|
||||
F: FnMut() -> V + 'static,
|
||||
V: InnerHtmlValue<R>,
|
||||
V::State: 'static,
|
||||
R: DomRenderer,
|
||||
{
|
||||
type State = RenderEffectState<V::State>;
|
||||
#[cfg(not(feature = "nightly"))]
|
||||
mod stable {
|
||||
use super::RenderEffectState;
|
||||
use crate::{
|
||||
html::attribute::{Attribute, AttributeValue},
|
||||
hydration::Cursor,
|
||||
renderer::Renderer,
|
||||
ssr::StreamBuilder,
|
||||
view::{Position, PositionState, Render, RenderHtml},
|
||||
};
|
||||
use any_error::Error as AnyError;
|
||||
use reactive_graph::{
|
||||
computed::{ArcMemo, Memo},
|
||||
signal::{ArcReadSignal, ArcRwSignal, ReadSignal, RwSignal},
|
||||
traits::Get,
|
||||
wrappers::read::{ArcSignal, Signal},
|
||||
};
|
||||
|
||||
fn html_len(&self) -> usize {
|
||||
0
|
||||
}
|
||||
macro_rules! signal_impl {
|
||||
($sig:ident) => {
|
||||
impl<V, R> Render<R> for $sig<V>
|
||||
where
|
||||
V: Render<R> + Clone + Send + Sync + 'static,
|
||||
V::State: 'static,
|
||||
V::FallibleState: 'static,
|
||||
R: Renderer,
|
||||
{
|
||||
type State = RenderEffectState<V::State>;
|
||||
type FallibleState = RenderEffectState<
|
||||
Result<V::FallibleState, Option<AnyError>>,
|
||||
>;
|
||||
// TODO how this should be handled?
|
||||
type AsyncOutput = Self;
|
||||
|
||||
fn to_html(mut self, buf: &mut String) {
|
||||
let value = self();
|
||||
value.to_html(buf);
|
||||
}
|
||||
#[track_caller]
|
||||
fn build(self) -> Self::State {
|
||||
(move || self.get()).build()
|
||||
}
|
||||
|
||||
fn to_template(_buf: &mut String) {}
|
||||
fn try_build(self) -> any_error::Result<Self::FallibleState> {
|
||||
(move || self.get()).try_build()
|
||||
}
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
mut self,
|
||||
el: &<R as Renderer>::Element,
|
||||
) -> Self::State {
|
||||
let el = el.to_owned();
|
||||
RenderEffect::new(move |prev| {
|
||||
let value = self();
|
||||
if let Some(mut state) = prev {
|
||||
value.rebuild(&mut state);
|
||||
state
|
||||
} else {
|
||||
value.hydrate::<FROM_SERVER>(&el)
|
||||
#[track_caller]
|
||||
fn rebuild(self, _state: &mut Self::State) {
|
||||
// TODO rebuild
|
||||
}
|
||||
|
||||
fn try_rebuild(
|
||||
self,
|
||||
state: &mut Self::FallibleState,
|
||||
) -> any_error::Result<()> {
|
||||
(move || self.get()).try_rebuild(state)
|
||||
}
|
||||
|
||||
async fn resolve(self) -> Self::AsyncOutput {
|
||||
self
|
||||
}
|
||||
}
|
||||
})
|
||||
.into()
|
||||
}
|
||||
|
||||
fn build(mut self, el: &<R as Renderer>::Element) -> Self::State {
|
||||
let el = el.to_owned();
|
||||
RenderEffect::new(move |prev| {
|
||||
let value = self();
|
||||
if let Some(mut state) = prev {
|
||||
value.rebuild(&mut state);
|
||||
state
|
||||
} else {
|
||||
value.build(&el)
|
||||
impl<V, R> RenderHtml<R> for $sig<V>
|
||||
where
|
||||
V: RenderHtml<R> + Clone + Send + Sync + 'static,
|
||||
V::State: 'static,
|
||||
V::FallibleState: 'static,
|
||||
R: Renderer + 'static,
|
||||
{
|
||||
const MIN_LENGTH: usize = 0;
|
||||
|
||||
fn html_len(&self) -> usize {
|
||||
V::MIN_LENGTH
|
||||
}
|
||||
|
||||
fn to_html_with_buf(
|
||||
self,
|
||||
buf: &mut String,
|
||||
position: &mut Position,
|
||||
) {
|
||||
let value = self.get();
|
||||
value.to_html_with_buf(buf, position)
|
||||
}
|
||||
|
||||
fn to_html_async_with_buf<const OUT_OF_ORDER: bool>(
|
||||
self,
|
||||
buf: &mut StreamBuilder,
|
||||
position: &mut Position,
|
||||
) where
|
||||
Self: Sized,
|
||||
{
|
||||
let value = self.get();
|
||||
value.to_html_async_with_buf::<OUT_OF_ORDER>(buf, position);
|
||||
}
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
cursor: &Cursor<R>,
|
||||
position: &PositionState,
|
||||
) -> Self::State {
|
||||
(move || self.get())
|
||||
.hydrate::<FROM_SERVER>(cursor, position)
|
||||
}
|
||||
}
|
||||
})
|
||||
.into()
|
||||
|
||||
impl<V, R> AttributeValue<R> for $sig<V>
|
||||
where
|
||||
V: AttributeValue<R> + Clone + Send + Sync + 'static,
|
||||
V::State: 'static,
|
||||
R: Renderer,
|
||||
{
|
||||
type State = RenderEffectState<V::State>;
|
||||
|
||||
fn html_len(&self) -> usize {
|
||||
0
|
||||
}
|
||||
|
||||
fn to_html(self, key: &str, buf: &mut String) {
|
||||
let value = self.get();
|
||||
value.to_html(key, buf);
|
||||
}
|
||||
|
||||
fn to_template(_key: &str, _buf: &mut String) {}
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
mut self,
|
||||
key: &str,
|
||||
el: &<R as Renderer>::Element,
|
||||
) -> Self::State {
|
||||
(move || self.get()).hydrate::<FROM_SERVER>(key, el)
|
||||
}
|
||||
|
||||
fn build(
|
||||
self,
|
||||
el: &<R as Renderer>::Element,
|
||||
key: &str,
|
||||
) -> Self::State {
|
||||
(move || self.get()).build(el, key)
|
||||
}
|
||||
|
||||
fn rebuild(self, _key: &str, _state: &mut Self::State) {
|
||||
// TODO rebuild
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn rebuild(self, _state: &mut Self::State) {}
|
||||
macro_rules! signal_impl_unsend {
|
||||
($sig:ident) => {
|
||||
impl<V, R> Render<R> for $sig<V>
|
||||
where
|
||||
V: Render<R> + Clone + 'static,
|
||||
V::State: 'static,
|
||||
V::FallibleState: 'static,
|
||||
R: Renderer,
|
||||
{
|
||||
type State = RenderEffectState<V::State>;
|
||||
type FallibleState = RenderEffectState<
|
||||
Result<V::FallibleState, Option<AnyError>>,
|
||||
>;
|
||||
// TODO how this should be handled?
|
||||
type AsyncOutput = Self;
|
||||
|
||||
#[track_caller]
|
||||
fn build(self) -> Self::State {
|
||||
(move || self.get()).build()
|
||||
}
|
||||
|
||||
fn try_build(self) -> any_error::Result<Self::FallibleState> {
|
||||
(move || self.get()).try_build()
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn rebuild(self, _state: &mut Self::State) {
|
||||
// TODO rebuild
|
||||
}
|
||||
|
||||
fn try_rebuild(
|
||||
self,
|
||||
state: &mut Self::FallibleState,
|
||||
) -> any_error::Result<()> {
|
||||
(move || self.get()).try_rebuild(state)
|
||||
}
|
||||
|
||||
async fn resolve(self) -> Self::AsyncOutput {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<V, R> RenderHtml<R> for $sig<V>
|
||||
where
|
||||
V: RenderHtml<R> + Clone + Send + Sync + 'static,
|
||||
V::State: 'static,
|
||||
V::FallibleState: 'static,
|
||||
R: Renderer + 'static,
|
||||
{
|
||||
const MIN_LENGTH: usize = 0;
|
||||
|
||||
fn html_len(&self) -> usize {
|
||||
V::MIN_LENGTH
|
||||
}
|
||||
|
||||
fn to_html_with_buf(
|
||||
self,
|
||||
buf: &mut String,
|
||||
position: &mut Position,
|
||||
) {
|
||||
let value = self.get();
|
||||
value.to_html_with_buf(buf, position)
|
||||
}
|
||||
|
||||
fn to_html_async_with_buf<const OUT_OF_ORDER: bool>(
|
||||
self,
|
||||
buf: &mut StreamBuilder,
|
||||
position: &mut Position,
|
||||
) where
|
||||
Self: Sized,
|
||||
{
|
||||
let value = self.get();
|
||||
value.to_html_async_with_buf::<OUT_OF_ORDER>(buf, position);
|
||||
}
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
cursor: &Cursor<R>,
|
||||
position: &PositionState,
|
||||
) -> Self::State {
|
||||
(move || self.get())
|
||||
.hydrate::<FROM_SERVER>(cursor, position)
|
||||
}
|
||||
}
|
||||
|
||||
impl<V, R> AttributeValue<R> for $sig<V>
|
||||
where
|
||||
V: AttributeValue<R> + Clone + 'static,
|
||||
V::State: 'static,
|
||||
R: Renderer,
|
||||
{
|
||||
type State = RenderEffectState<V::State>;
|
||||
|
||||
fn html_len(&self) -> usize {
|
||||
0
|
||||
}
|
||||
|
||||
fn to_html(self, key: &str, buf: &mut String) {
|
||||
let value = self.get();
|
||||
value.to_html(key, buf);
|
||||
}
|
||||
|
||||
fn to_template(_key: &str, _buf: &mut String) {}
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
mut self,
|
||||
key: &str,
|
||||
el: &<R as Renderer>::Element,
|
||||
) -> Self::State {
|
||||
(move || self.get()).hydrate::<FROM_SERVER>(key, el)
|
||||
}
|
||||
|
||||
fn build(
|
||||
self,
|
||||
el: &<R as Renderer>::Element,
|
||||
key: &str,
|
||||
) -> Self::State {
|
||||
(move || self.get()).build(el, key)
|
||||
}
|
||||
|
||||
fn rebuild(self, _key: &str, _state: &mut Self::State) {
|
||||
// TODO rebuild
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
signal_impl!(RwSignal);
|
||||
signal_impl!(ReadSignal);
|
||||
signal_impl!(Memo);
|
||||
signal_impl!(Signal);
|
||||
signal_impl_unsend!(ArcRwSignal);
|
||||
signal_impl_unsend!(ArcReadSignal);
|
||||
signal_impl!(ArcMemo);
|
||||
signal_impl!(ArcSignal);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -0,0 +1,150 @@
|
|||
use crate::{
|
||||
html::property::IntoProperty,
|
||||
renderer::{DomRenderer, Renderer},
|
||||
};
|
||||
use reactive_graph::effect::RenderEffect;
|
||||
|
||||
// These do update during hydration because properties don't exist in the DOM
|
||||
impl<F, V, R> IntoProperty<R> for F
|
||||
where
|
||||
F: FnMut() -> V + 'static,
|
||||
V: IntoProperty<R>,
|
||||
V::State: 'static,
|
||||
R: DomRenderer,
|
||||
{
|
||||
type State = RenderEffect<V::State>;
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
mut self,
|
||||
el: &<R as Renderer>::Element,
|
||||
key: &str,
|
||||
) -> Self::State {
|
||||
let key = R::intern(key);
|
||||
let key = key.to_owned();
|
||||
let el = el.to_owned();
|
||||
|
||||
RenderEffect::new(move |prev| {
|
||||
let value = self();
|
||||
if let Some(mut state) = prev {
|
||||
value.rebuild(&mut state, &key);
|
||||
state
|
||||
} else {
|
||||
value.hydrate::<FROM_SERVER>(&el, &key)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn build(
|
||||
mut self,
|
||||
el: &<R as Renderer>::Element,
|
||||
key: &str,
|
||||
) -> Self::State {
|
||||
let key = R::intern(key);
|
||||
let key = key.to_owned();
|
||||
let el = el.to_owned();
|
||||
|
||||
RenderEffect::new(move |prev| {
|
||||
let value = self();
|
||||
if let Some(mut state) = prev {
|
||||
value.rebuild(&mut state, &key);
|
||||
state
|
||||
} else {
|
||||
value.build(&el, &key)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn rebuild(self, _state: &mut Self::State, _key: &str) {
|
||||
// TODO rebuild
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "nightly"))]
|
||||
mod stable {
|
||||
use crate::{
|
||||
html::property::IntoProperty,
|
||||
renderer::{DomRenderer, Renderer},
|
||||
};
|
||||
use reactive_graph::{
|
||||
computed::{ArcMemo, Memo},
|
||||
effect::RenderEffect,
|
||||
signal::{ArcReadSignal, ArcRwSignal, ReadSignal, RwSignal},
|
||||
traits::Get,
|
||||
wrappers::read::{ArcSignal, Signal},
|
||||
};
|
||||
|
||||
macro_rules! property_signal {
|
||||
($sig:ident) => {
|
||||
impl<V, R> IntoProperty<R> for $sig<V>
|
||||
where
|
||||
V: IntoProperty<R> + Send + Sync + Clone + 'static,
|
||||
V::State: 'static,
|
||||
R: DomRenderer,
|
||||
{
|
||||
type State = RenderEffect<V::State>;
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
el: &<R as Renderer>::Element,
|
||||
key: &str,
|
||||
) -> Self::State {
|
||||
(move || self.get()).hydrate::<FROM_SERVER>(el, key)
|
||||
}
|
||||
|
||||
fn build(
|
||||
self,
|
||||
el: &<R as Renderer>::Element,
|
||||
key: &str,
|
||||
) -> Self::State {
|
||||
(move || self.get()).build(el, key)
|
||||
}
|
||||
|
||||
fn rebuild(self, _state: &mut Self::State, _key: &str) {
|
||||
// TODO rebuild
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! property_signal_unsend {
|
||||
($sig:ident) => {
|
||||
impl<V, R> IntoProperty<R> for $sig<V>
|
||||
where
|
||||
V: IntoProperty<R> + Clone + 'static,
|
||||
V::State: 'static,
|
||||
R: DomRenderer,
|
||||
{
|
||||
type State = RenderEffect<V::State>;
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
el: &<R as Renderer>::Element,
|
||||
key: &str,
|
||||
) -> Self::State {
|
||||
(move || self.get()).hydrate::<FROM_SERVER>(el, key)
|
||||
}
|
||||
|
||||
fn build(
|
||||
self,
|
||||
el: &<R as Renderer>::Element,
|
||||
key: &str,
|
||||
) -> Self::State {
|
||||
(move || self.get()).build(el, key)
|
||||
}
|
||||
|
||||
fn rebuild(self, _state: &mut Self::State, _key: &str) {
|
||||
// TODO rebuild
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
property_signal!(RwSignal);
|
||||
property_signal!(ReadSignal);
|
||||
property_signal!(Memo);
|
||||
property_signal!(Signal);
|
||||
property_signal_unsend!(ArcRwSignal);
|
||||
property_signal_unsend!(ArcReadSignal);
|
||||
property_signal!(ArcMemo);
|
||||
property_signal!(ArcSignal);
|
||||
}
|
|
@ -1,4 +1,3 @@
|
|||
use super::RenderEffectState;
|
||||
use crate::{html::style::IntoStyle, renderer::DomRenderer};
|
||||
use reactive_graph::effect::RenderEffect;
|
||||
use std::borrow::Cow;
|
||||
|
@ -9,7 +8,7 @@ where
|
|||
S: Into<Cow<'static, str>>,
|
||||
R: DomRenderer,
|
||||
{
|
||||
type State = RenderEffectState<(R::CssStyleDeclaration, Cow<'static, str>)>;
|
||||
type State = RenderEffect<(R::CssStyleDeclaration, Cow<'static, str>)>;
|
||||
|
||||
fn to_html(self, style: &mut String) {
|
||||
let (name, mut f) = self;
|
||||
|
@ -75,30 +74,7 @@ where
|
|||
}
|
||||
|
||||
fn rebuild(self, _state: &mut Self::State) {
|
||||
// TODO — knowing how and whether to rebuild effects like this is tricky
|
||||
// it's the one place I've run into "stale values" when experimenting with this model
|
||||
|
||||
/* let (name, mut f) = self;
|
||||
let prev_effect = std::mem::take(&mut state.0);
|
||||
let prev_value = prev_effect.as_ref().and_then(|e| e.take_value());
|
||||
drop(prev_effect);
|
||||
*state = RenderEffect::new_with_value(
|
||||
move |prev| {
|
||||
let value = f().into();
|
||||
if let Some(mut state) = prev {
|
||||
let (style, prev) = &mut state;
|
||||
if &value != prev {
|
||||
R::set_css_property(&style, name, &value);
|
||||
}
|
||||
*prev = value;
|
||||
state
|
||||
} else {
|
||||
todo!()
|
||||
}
|
||||
},
|
||||
prev_value,
|
||||
)
|
||||
.into(); */
|
||||
// TODO rebuild
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -111,9 +87,9 @@ where
|
|||
{
|
||||
type State = RenderEffect<C::State>;
|
||||
|
||||
fn to_html(mut self, class: &mut String) {
|
||||
fn to_html(mut self, style: &mut String) {
|
||||
let value = self();
|
||||
value.to_html(class);
|
||||
value.to_html(style);
|
||||
}
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
|
@ -139,3 +115,159 @@ where
|
|||
|
||||
fn rebuild(self, _state: &mut Self::State) {}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "nightly"))]
|
||||
mod stable {
|
||||
macro_rules! style_signal {
|
||||
($sig:ident) => {
|
||||
impl<C, R> IntoStyle<R> for $sig<C>
|
||||
where
|
||||
C: IntoStyle<R> + Clone + Send + Sync + 'static,
|
||||
C::State: 'static,
|
||||
R: DomRenderer,
|
||||
{
|
||||
type State = RenderEffect<C::State>;
|
||||
|
||||
fn to_html(self, style: &mut String) {
|
||||
let value = self.get();
|
||||
value.to_html(style);
|
||||
}
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
el: &R::Element,
|
||||
) -> Self::State {
|
||||
(move || self.get()).hydrate::<FROM_SERVER>(el)
|
||||
}
|
||||
|
||||
fn build(self, el: &R::Element) -> Self::State {
|
||||
(move || self.get()).build(el)
|
||||
}
|
||||
|
||||
fn rebuild(self, _state: &mut Self::State) {
|
||||
// TODO rebuild here?
|
||||
}
|
||||
}
|
||||
|
||||
impl<R, S> IntoStyle<R> for (&'static str, $sig<S>)
|
||||
where
|
||||
S: Into<Cow<'static, str>> + Send + Sync + Clone + 'static,
|
||||
R: DomRenderer,
|
||||
{
|
||||
type State =
|
||||
RenderEffect<(R::CssStyleDeclaration, Cow<'static, str>)>;
|
||||
|
||||
fn to_html(self, style: &mut String) {
|
||||
IntoStyle::<R>::to_html(
|
||||
(self.0, move || self.1.get()),
|
||||
style,
|
||||
)
|
||||
}
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
el: &R::Element,
|
||||
) -> Self::State {
|
||||
IntoStyle::<R>::hydrate::<FROM_SERVER>(
|
||||
(self.0, move || self.1.get()),
|
||||
el,
|
||||
)
|
||||
}
|
||||
|
||||
fn build(self, el: &R::Element) -> Self::State {
|
||||
IntoStyle::<R>::build((self.0, move || self.1.get()), el)
|
||||
}
|
||||
|
||||
fn rebuild(self, _state: &mut Self::State) {
|
||||
// TODO rebuild here?
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! style_signal_unsend {
|
||||
($sig:ident) => {
|
||||
impl<C, R> IntoStyle<R> for $sig<C>
|
||||
where
|
||||
C: IntoStyle<R> + Clone + 'static,
|
||||
C::State: 'static,
|
||||
R: DomRenderer,
|
||||
{
|
||||
type State = RenderEffect<C::State>;
|
||||
|
||||
fn to_html(self, style: &mut String) {
|
||||
let value = self.get();
|
||||
value.to_html(style);
|
||||
}
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
el: &R::Element,
|
||||
) -> Self::State {
|
||||
(move || self.get()).hydrate::<FROM_SERVER>(el)
|
||||
}
|
||||
|
||||
fn build(self, el: &R::Element) -> Self::State {
|
||||
(move || self.get()).build(el)
|
||||
}
|
||||
|
||||
fn rebuild(self, _state: &mut Self::State) {
|
||||
// TODO rebuild here?
|
||||
}
|
||||
}
|
||||
|
||||
impl<R, S> IntoStyle<R> for (&'static str, $sig<S>)
|
||||
where
|
||||
S: Into<Cow<'static, str>> + Send + Sync + Clone + 'static,
|
||||
R: DomRenderer,
|
||||
{
|
||||
type State =
|
||||
RenderEffect<(R::CssStyleDeclaration, Cow<'static, str>)>;
|
||||
|
||||
fn to_html(self, style: &mut String) {
|
||||
IntoStyle::<R>::to_html(
|
||||
(self.0, move || self.1.get()),
|
||||
style,
|
||||
)
|
||||
}
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
el: &R::Element,
|
||||
) -> Self::State {
|
||||
IntoStyle::<R>::hydrate::<FROM_SERVER>(
|
||||
(self.0, move || self.1.get()),
|
||||
el,
|
||||
)
|
||||
}
|
||||
|
||||
fn build(self, el: &R::Element) -> Self::State {
|
||||
IntoStyle::<R>::build((self.0, move || self.1.get()), el)
|
||||
}
|
||||
|
||||
fn rebuild(self, _state: &mut Self::State) {
|
||||
// TODO rebuild here?
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
use super::RenderEffect;
|
||||
use crate::{html::style::IntoStyle, renderer::DomRenderer};
|
||||
use reactive_graph::{
|
||||
computed::{ArcMemo, Memo},
|
||||
signal::{ArcReadSignal, ArcRwSignal, ReadSignal, RwSignal},
|
||||
traits::Get,
|
||||
wrappers::read::{ArcSignal, Signal},
|
||||
};
|
||||
use std::borrow::Cow;
|
||||
|
||||
style_signal!(RwSignal);
|
||||
style_signal!(ReadSignal);
|
||||
style_signal!(Memo);
|
||||
style_signal!(Signal);
|
||||
style_signal_unsend!(ArcRwSignal);
|
||||
style_signal_unsend!(ArcReadSignal);
|
||||
style_signal!(ArcMemo);
|
||||
style_signal!(ArcSignal);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue