Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AddAnyAttr for AnyView for non-erased #3553

Merged
merged 1 commit into from
Feb 6, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
493 changes: 262 additions & 231 deletions Cargo.lock

Large diffs are not rendered by default.

14 changes: 7 additions & 7 deletions examples/counter/tests/web.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ async fn clear() {
// note that we start at the initial value of 10
let _dispose = mount_to(
test_wrapper.clone().unchecked_into(),
|| view! { <SimpleCounter initial_value=10 step=1/> },
|| view! { <SimpleCounter initial_value=10 step=1 /> },
);

// now we extract the buttons by iterating over the DOM
Expand Down Expand Up @@ -59,9 +59,9 @@ async fn clear() {
// .into_view() here is just a convenient way of specifying "use the regular DOM renderer"
.into_view()
// views are lazy -- they describe a DOM tree but don't create it yet
// calling .build() will actually build the DOM elements
.build()
// .build() returned an ElementState, which is a smart pointer for
// calling .build(None) will actually build the DOM elements
.build(None)
// .build(None) returned an ElementState, which is a smart pointer for
// a DOM element. So we can still just call .outer_html(), which access the outerHTML on
// the actual DOM element
.outer_html()
Expand All @@ -87,7 +87,7 @@ async fn inc() {

let _dispose = mount_to(
test_wrapper.clone().unchecked_into(),
|| view! { <SimpleCounter initial_value=0 step=1/> },
|| view! { <SimpleCounter initial_value=0 step=1 /> },
);

// You can do testing with vanilla DOM operations
Expand Down Expand Up @@ -150,7 +150,7 @@ async fn inc() {
}
}
.into_view()
.build()
.build(None)
.outer_html()
);

Expand All @@ -173,7 +173,7 @@ async fn inc() {
}
}
.into_view()
.build()
.build(None)
.outer_html()
);
}
51 changes: 38 additions & 13 deletions leptos/src/attribute_interceptor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use crate::attr::{
Attribute, NextAttribute,
};
use leptos::prelude::*;
use tachys::view::any_view::ExtraAttrsMut;

/// Function stored to build/rebuild the wrapped children when attributes are added.
type ChildBuilder<T> = dyn Fn(AnyAttribute) -> T + Send + Sync + 'static;
Expand Down Expand Up @@ -43,7 +44,7 @@ pub fn AttributeInterceptor<Chil, T>(
) -> impl IntoView
where
Chil: Fn(AnyAttribute) -> T + Send + Sync + 'static,
T: IntoView,
T: IntoView + 'static,
{
AttributeInterceptorInner::new(children)
}
Expand Down Expand Up @@ -77,16 +78,20 @@ impl<T: IntoView> AttributeInterceptorInner<T, ()> {
impl<T: IntoView, A: Attribute> Render for AttributeInterceptorInner<T, A> {
type State = <T as Render>::State;

fn build(self) -> Self::State {
self.children.build()
fn build(self, extra_attrs: Option<Vec<AnyAttribute>>) -> Self::State {
self.children.build(extra_attrs)
}

fn rebuild(self, state: &mut Self::State) {
self.children.rebuild(state);
fn rebuild(
self,
state: &mut Self::State,
extra_attrs: Option<Vec<AnyAttribute>>,
) {
self.children.rebuild(state, extra_attrs);
}
}

impl<T: IntoView, A> AddAnyAttr for AttributeInterceptorInner<T, A>
impl<T: IntoView + 'static, A> AddAnyAttr for AttributeInterceptorInner<T, A>
where
A: Attribute,
{
Expand Down Expand Up @@ -114,19 +119,23 @@ where
}
}

impl<T: IntoView, A: Attribute> RenderHtml for AttributeInterceptorInner<T, A> {
impl<T: IntoView + 'static, A: Attribute> RenderHtml
for AttributeInterceptorInner<T, A>
{
type AsyncOutput = T::AsyncOutput;
type Owned = AttributeInterceptorInner<T, A::CloneableOwned>;

const MIN_LENGTH: usize = T::MIN_LENGTH;

fn dry_resolve(&mut self) {
self.children.dry_resolve()
fn dry_resolve(&mut self, extra_attrs: ExtraAttrsMut<'_>) {
self.children.dry_resolve(extra_attrs)
}

fn resolve(
self,
extra_attrs: ExtraAttrsMut<'_>,
) -> impl std::future::Future<Output = Self::AsyncOutput> + Send {
self.children.resolve()
self.children.resolve(extra_attrs)
}

fn to_html_with_buf(
Expand All @@ -135,16 +144,32 @@ impl<T: IntoView, A: Attribute> RenderHtml for AttributeInterceptorInner<T, A> {
position: &mut leptos::tachys::view::Position,
escape: bool,
mark_branches: bool,
extra_attrs: Option<Vec<AnyAttribute>>,
) {
self.children
.to_html_with_buf(buf, position, escape, mark_branches)
self.children.to_html_with_buf(
buf,
position,
escape,
mark_branches,
extra_attrs,
)
}

fn hydrate<const FROM_SERVER: bool>(
self,
cursor: &leptos::tachys::hydration::Cursor,
position: &leptos::tachys::view::PositionState,
extra_attrs: Option<Vec<AnyAttribute>>,
) -> Self::State {
self.children.hydrate::<FROM_SERVER>(cursor, position)
self.children
.hydrate::<FROM_SERVER>(cursor, position, extra_attrs)
}

fn into_owned(self) -> Self::Owned {
AttributeInterceptorInner {
children_builder: self.children_builder,
children: self.children,
attributes: self.attributes.into_cloneable_owned(),
}
}
}
67 changes: 49 additions & 18 deletions leptos/src/error_boundary.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@ use reactive_graph::{
use rustc_hash::FxHashMap;
use std::{fmt::Debug, sync::Arc};
use tachys::{
html::attribute::Attribute,
html::attribute::{any_attribute::AnyAttribute, Attribute},
hydration::Cursor,
reactive_graph::OwnedView,
ssr::StreamBuilder,
view::{
add_attr::AddAnyAttr, Mountable, Position, PositionState, Render,
RenderHtml,
add_attr::AddAnyAttr, any_view::ExtraAttrsMut, Mountable, Position,
PositionState, Render, RenderHtml,
},
};
use throw_error::{Error, ErrorHook, ErrorId};
Expand Down Expand Up @@ -173,10 +173,10 @@ where
{
type State = RenderEffect<ErrorBoundaryViewState<Chil::State, Fal::State>>;

fn build(mut self) -> Self::State {
fn build(mut self, extra_attrs: Option<Vec<AnyAttribute>>) -> Self::State {
let hook = Arc::clone(&self.hook);
let _hook = throw_error::set_error_hook(Arc::clone(&hook));
let mut children = Some(self.children.build());
let mut children = Some(self.children.build(extra_attrs.clone()));
RenderEffect::new(
move |prev: Option<
ErrorBoundaryViewState<Chil::State, Fal::State>,
Expand All @@ -193,7 +193,8 @@ where
// yes errors, and was showing children
(false, None) => {
state.fallback = Some(
(self.fallback)(self.errors.clone()).build(),
(self.fallback)(self.errors.clone())
.build(extra_attrs.clone()),
);
state
.children
Expand All @@ -207,8 +208,10 @@ where
}
state
} else {
let fallback = (!self.errors_empty.get())
.then(|| (self.fallback)(self.errors.clone()).build());
let fallback = (!self.errors_empty.get()).then(|| {
(self.fallback)(self.errors.clone())
.build(extra_attrs.clone())
});
ErrorBoundaryViewState {
children: children.take().unwrap(),
fallback,
Expand All @@ -218,8 +221,12 @@ where
)
}

fn rebuild(self, state: &mut Self::State) {
let new = self.build();
fn rebuild(
self,
state: &mut Self::State,
extra_attrs: Option<Vec<AnyAttribute>>,
) {
let new = self.build(extra_attrs);
let mut old = std::mem::replace(state, new);
old.insert_before_this(state);
old.unmount();
Expand Down Expand Up @@ -268,14 +275,18 @@ where
Fal: RenderHtml + Send + 'static,
{
type AsyncOutput = ErrorBoundaryView<Chil::AsyncOutput, FalFn>;
type Owned = Self;

const MIN_LENGTH: usize = Chil::MIN_LENGTH;

fn dry_resolve(&mut self) {
self.children.dry_resolve();
fn dry_resolve(&mut self, extra_attrs: ExtraAttrsMut<'_>) {
self.children.dry_resolve(extra_attrs);
}

async fn resolve(self) -> Self::AsyncOutput {
async fn resolve(
self,
extra_attrs: ExtraAttrsMut<'_>,
) -> Self::AsyncOutput {
let ErrorBoundaryView {
hook,
boundary_id,
Expand All @@ -289,7 +300,7 @@ where
hook,
boundary_id,
errors_empty,
children: children.resolve().await,
children: children.resolve(extra_attrs).await,
fallback,
errors,
}
Expand All @@ -301,6 +312,7 @@ where
position: &mut Position,
escape: bool,
mark_branches: bool,
extra_attrs: Option<Vec<AnyAttribute>>,
) {
// first, attempt to serialize the children to HTML, then check for errors
let _hook = throw_error::set_error_hook(self.hook);
Expand All @@ -311,6 +323,7 @@ where
&mut new_pos,
escape,
mark_branches,
extra_attrs.clone(),
);

// any thrown errors would've been caught here
Expand All @@ -323,6 +336,7 @@ where
position,
escape,
mark_branches,
extra_attrs,
);
}
}
Expand All @@ -333,6 +347,7 @@ where
position: &mut Position,
escape: bool,
mark_branches: bool,
extra_attrs: Option<Vec<AnyAttribute>>,
) where
Self: Sized,
{
Expand All @@ -345,6 +360,7 @@ where
&mut new_pos,
escape,
mark_branches,
extra_attrs.clone(),
);

// any thrown errors would've been caught here
Expand All @@ -358,6 +374,7 @@ where
position,
escape,
mark_branches,
extra_attrs,
);
buf.push_sync(&fallback);
}
Expand All @@ -367,6 +384,7 @@ where
mut self,
cursor: &Cursor,
position: &PositionState,
extra_attrs: Option<Vec<AnyAttribute>>,
) -> Self::State {
let mut children = Some(self.children);
let hook = Arc::clone(&self.hook);
Expand All @@ -388,7 +406,8 @@ where
// yes errors, and was showing children
(false, None) => {
state.fallback = Some(
(self.fallback)(self.errors.clone()).build(),
(self.fallback)(self.errors.clone())
.build(extra_attrs.clone()),
);
state
.children
Expand All @@ -405,15 +424,23 @@ where
let children = children.take().unwrap();
let (children, fallback) = if self.errors_empty.get() {
(
children.hydrate::<FROM_SERVER>(&cursor, &position),
children.hydrate::<FROM_SERVER>(
&cursor,
&position,
extra_attrs.clone(),
),
None,
)
} else {
(
children.build(),
children.build(extra_attrs.clone()),
Some(
(self.fallback)(self.errors.clone())
.hydrate::<FROM_SERVER>(&cursor, &position),
.hydrate::<FROM_SERVER>(
&cursor,
&position,
extra_attrs.clone(),
),
),
)
};
Expand All @@ -423,6 +450,10 @@ where
},
)
}

fn into_owned(self) -> Self::Owned {
self
}
}

#[derive(Debug)]
Expand Down
Loading