feat: iterating over items in children with ChildrenFragment, ChildrenFragmentFn, ChildrenFragmentMut

This commit is contained in:
Greg Johnston 2024-05-15 19:46:51 -04:00
parent f5d203f0c9
commit 0dd1932b7f
3 changed files with 158 additions and 1 deletions

View file

@ -7,22 +7,39 @@ use tachys::{
renderer::dom::Dom,
view::{
any_view::{AnyView, IntoAny},
fragment::{Fragment, IntoFragment},
RenderHtml,
},
};
/// The most common type for the `children` property on components,
/// which can only be called once.
///
/// This does not support iterating over individual nodes within the children.
/// To iterate over children, use [`ChildrenFragment`].
pub type Children = Box<dyn FnOnce() -> AnyView<Dom> + Send>;
/// A type for the `children` property on components that can be called only once,
/// and provides a collection of all the children passed to this component.
pub type ChildrenFragment = Box<dyn FnOnce() -> Fragment<Dom> + Send>;
/// A type for the `children` property on components that can be called
/// more than once.
pub type ChildrenFn = Arc<dyn Fn() -> AnyView<Dom> + Send + Sync>;
/// A type for the `children` property on components that can be called more than once,
/// and provides a collection of all the children passed to this component.
pub type ChildrenFragmentFn = Arc<dyn Fn() -> Fragment<Dom> + Send>;
/// A type for the `children` property on components that can be called
/// more than once, but may mutate the children.
pub type ChildrenFnMut = Box<dyn FnMut() -> AnyView<Dom> + Send>;
/// A type for the `children` property on components that can be called more than once,
/// but may mutate the children, and provides a collection of all the children
/// passed to this component.
pub type ChildrenFragmentMut = Box<dyn FnMut() -> Fragment<Dom> + Send>;
// This is to still support components that accept `Box<dyn Fn() -> AnyView>` as a children.
type BoxedChildrenFn = Box<dyn Fn() -> AnyView<Dom> + Send>;
@ -147,6 +164,39 @@ where
}
}
impl<F, C> ToChildren<F> for ChildrenFragment
where
F: FnOnce() -> C + Send + 'static,
C: IntoFragment<Dom>,
{
#[inline]
fn to_children(f: F) -> Self {
Box::new(move || f().into_fragment())
}
}
impl<F, C> ToChildren<F> for ChildrenFragmentFn
where
F: Fn() -> C + Send + 'static,
C: IntoFragment<Dom>,
{
#[inline]
fn to_children(f: F) -> Self {
Arc::new(move || f().into_fragment())
}
}
impl<F, C> ToChildren<F> for ChildrenFragmentMut
where
F: FnMut() -> C + Send + 'static,
C: IntoFragment<Dom>,
{
#[inline]
fn to_children(mut f: F) -> Self {
Box::new(move || f().into_fragment())
}
}
/// New-type wrapper for a function that returns a view with `From` and `Default` traits implemented
/// to enable optional props in for example `<Show>` and `<Suspense>`.
#[derive(Clone)]

106
tachys/src/view/fragment.rs Normal file
View file

@ -0,0 +1,106 @@
use super::any_view::{AnyView, IntoAny};
use crate::renderer::Renderer;
/// A typed-erased collection of different views.
pub struct Fragment<R: Renderer> {
/// The nodes contained in the fragment.
pub nodes: Vec<AnyView<R>>,
}
pub trait IntoFragment<R: Renderer> {
fn into_fragment(self) -> Fragment<R>;
}
impl<R: Renderer> FromIterator<AnyView<R>> for Fragment<R> {
fn from_iter<T: IntoIterator<Item = AnyView<R>>>(iter: T) -> Self {
Fragment::new(iter.into_iter().collect())
}
}
impl<R: Renderer> From<AnyView<R>> for Fragment<R> {
fn from(view: AnyView<R>) -> Self {
Fragment::new(vec![view])
}
}
impl<R: Renderer> From<Fragment<R>> for AnyView<R> {
fn from(value: Fragment<R>) -> Self {
value.nodes.into_any()
}
}
impl<R: Renderer> Fragment<R> {
/// Creates a new [`Fragment`].
#[inline(always)]
pub fn new(nodes: Vec<AnyView<R>>) -> Self {
Self { nodes }
}
}
impl<T, R> IntoFragment<R> for Vec<T>
where
T: IntoAny<R>,
R: Renderer,
{
fn into_fragment(self) -> Fragment<R> {
Fragment::new(self.into_iter().map(IntoAny::into_any).collect())
}
}
impl<const N: usize, T, R> IntoFragment<R> for [T; N]
where
T: IntoAny<R>,
R: Renderer,
{
fn into_fragment(self) -> Fragment<R> {
Fragment::new(self.into_iter().map(IntoAny::into_any).collect())
}
}
macro_rules! tuples {
($($ty:ident),*) => {
impl<$($ty),*, Rndr> IntoFragment<Rndr> for ($($ty,)*)
where
$($ty: IntoAny<Rndr>),*,
Rndr: Renderer
{
fn into_fragment(self) -> Fragment<Rndr> {
#[allow(non_snake_case)]
let ($($ty,)*) = self;
Fragment::new(vec![$($ty.into_any(),)*])
}
}
}
}
tuples!(A);
tuples!(A, B);
tuples!(A, B, C);
tuples!(A, B, C, D);
tuples!(A, B, C, D, E);
tuples!(A, B, C, D, E, F);
tuples!(A, B, C, D, E, F, G);
tuples!(A, B, C, D, E, F, G, H);
tuples!(A, B, C, D, E, F, G, H, I);
tuples!(A, B, C, D, E, F, G, H, I, J);
tuples!(A, B, C, D, E, F, G, H, I, J, K);
tuples!(A, B, C, D, E, F, G, H, I, J, K, L);
tuples!(A, B, C, D, E, F, G, H, I, J, K, L, M);
tuples!(A, B, C, D, E, F, G, H, I, J, K, L, M, N);
tuples!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O);
tuples!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P);
tuples!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q);
tuples!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R);
tuples!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S);
tuples!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T);
tuples!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U);
tuples!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V);
tuples!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W);
tuples!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X);
tuples!(
A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y
);
tuples!(
A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y,
Z
);

View file

@ -1,4 +1,4 @@
use self::add_attr::AddAnyAttr;
use self::{add_attr::AddAnyAttr, fragment::Fragment};
use crate::{hydration::Cursor, renderer::Renderer, ssr::StreamBuilder};
use parking_lot::RwLock;
use std::{cell::RefCell, future::Future, rc::Rc, sync::Arc};
@ -7,6 +7,7 @@ pub mod add_attr;
pub mod any_view;
pub mod either;
pub mod error_boundary;
pub mod fragment;
pub mod iterators;
pub mod keyed;
mod primitives;