mirror of
https://github.com/DioxusLabs/dioxus
synced 2024-11-23 04:33:06 +00:00
add a lifetime to the properties trait and make the manual spread example work
This commit is contained in:
parent
fc8c25280a
commit
fa494349fe
7 changed files with 220 additions and 198 deletions
209
examples/spread.rs
Normal file
209
examples/spread.rs
Normal file
|
@ -0,0 +1,209 @@
|
|||
//! Example: SSR
|
||||
//!
|
||||
//! This example shows how we can render the Dioxus Virtualdom using SSR.
|
||||
|
||||
use dioxus::core::{Attribute, HasAttributesBox};
|
||||
use dioxus::html::{AudioExtension, ExtendedAudioMarker, ExtendedGlobalAttributesMarker};
|
||||
use dioxus::prelude::*;
|
||||
|
||||
fn main() {
|
||||
// We can render VirtualDoms
|
||||
let mut vdom = VirtualDom::new(app);
|
||||
let _ = vdom.rebuild();
|
||||
println!("{}", dioxus_ssr::render(&vdom));
|
||||
|
||||
// Or we can render rsx! calls themselves
|
||||
println!(
|
||||
"{}",
|
||||
dioxus_ssr::render_lazy(rsx! {
|
||||
div {
|
||||
h1 { "Hello, world!" }
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
// We can configure the SSR rendering to add ids for rehydration
|
||||
println!("{}", dioxus_ssr::pre_render(&vdom));
|
||||
|
||||
// We can render to a buf directly too
|
||||
let mut file = String::new();
|
||||
let mut renderer = dioxus_ssr::Renderer::default();
|
||||
renderer.render_to(&mut file, &vdom).unwrap();
|
||||
println!("{file}");
|
||||
}
|
||||
|
||||
fn app(cx: Scope) -> Element {
|
||||
cx.render(rsx!(Foo {
|
||||
autoplay: true,
|
||||
controls: true,
|
||||
}))
|
||||
}
|
||||
|
||||
pub struct FooProps<'a> {
|
||||
pub open: Option<&'a str>,
|
||||
attributes: Vec<Attribute<'a>>,
|
||||
}
|
||||
|
||||
// -----
|
||||
impl<'a> FooProps<'a> {
|
||||
#[doc = "\nCreate a builder for building `FooProps`.\nOn the builder, call `.open(...)`(optional) to set the values of the fields.\nFinally, call `.build()` to create the instance of `FooProps`.\n "]
|
||||
#[allow(dead_code)]
|
||||
pub fn builder(cx: &'a ScopeState) -> FooPropsBuilder<'a, ((),)> {
|
||||
FooPropsBuilder {
|
||||
bump: cx.bump(),
|
||||
fields: ((),),
|
||||
attributes: Vec::new(),
|
||||
_phantom: core::default::Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
#[must_use]
|
||||
#[doc(hidden)]
|
||||
#[allow(dead_code, non_camel_case_types, non_snake_case)]
|
||||
pub struct FooPropsBuilder<'a, TypedBuilderFields> {
|
||||
bump: &'a ::dioxus::core::exports::bumpalo::Bump,
|
||||
fields: TypedBuilderFields,
|
||||
attributes: Vec<Attribute<'a>>,
|
||||
_phantom: (core::marker::PhantomData<&'a ()>),
|
||||
}
|
||||
//impl<'a, TypedBuilderFields, > Clone for FooPropsBuilder<'a, TypedBuilderFields, > where TypedBuilderFields: Clone { fn clone(&self) -> Self { Self { fields: self.fields.clone(), attributes: self.attributes, _phantom: Default::default() } } }
|
||||
impl<'a> dioxus::prelude::Properties<'a> for FooProps<'a> {
|
||||
type Builder = FooPropsBuilder<'a, ((),)>;
|
||||
const IS_STATIC: bool = false;
|
||||
fn builder(cx: &'a ScopeState) -> Self::Builder {
|
||||
FooProps::builder(cx)
|
||||
}
|
||||
unsafe fn memoize(&self, other: &Self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
#[doc(hidden)]
|
||||
#[allow(dead_code, non_camel_case_types, non_snake_case)]
|
||||
pub trait FooPropsBuilder_Optional<T> {
|
||||
fn into_value<F: FnOnce() -> T>(self, default: F) -> T;
|
||||
}
|
||||
impl<T> FooPropsBuilder_Optional<T> for () {
|
||||
fn into_value<F: FnOnce() -> T>(self, default: F) -> T {
|
||||
default()
|
||||
}
|
||||
}
|
||||
impl<T> FooPropsBuilder_Optional<T> for (T,) {
|
||||
fn into_value<F: FnOnce() -> T>(self, _: F) -> T {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
#[allow(dead_code, non_camel_case_types, missing_docs)]
|
||||
impl<'a> FooPropsBuilder<'a, ((),)> {
|
||||
pub fn open(
|
||||
self,
|
||||
open: &'a str,
|
||||
) -> FooPropsBuilder<
|
||||
'a,
|
||||
((
|
||||
Option<&'a str>,
|
||||
// pub attributes: Vec<Attribute<'a>>,
|
||||
),),
|
||||
> {
|
||||
let open = (Some(open),);
|
||||
let (_,) = self.fields;
|
||||
FooPropsBuilder {
|
||||
bump: self.bump,
|
||||
fields: (open,),
|
||||
attributes: self.attributes,
|
||||
_phantom: self._phantom,
|
||||
}
|
||||
}
|
||||
}
|
||||
#[doc(hidden)]
|
||||
#[allow(dead_code, non_camel_case_types, non_snake_case)]
|
||||
pub enum FooPropsBuilder_Error_Repeated_field_open {}
|
||||
#[doc(hidden)]
|
||||
#[allow(dead_code, non_camel_case_types, missing_docs)]
|
||||
impl<'a>
|
||||
FooPropsBuilder<
|
||||
'a,
|
||||
((
|
||||
Option<&'a str>,
|
||||
// pub attributes: Vec<Attribute<'a>>,
|
||||
),),
|
||||
>
|
||||
{
|
||||
#[deprecated(note = "Repeated field open")]
|
||||
pub fn open(
|
||||
self,
|
||||
_: FooPropsBuilder_Error_Repeated_field_open,
|
||||
) -> FooPropsBuilder<
|
||||
'a,
|
||||
((
|
||||
Option<&'a str>,
|
||||
// pub attributes: Vec<Attribute<'a>>,
|
||||
),),
|
||||
> {
|
||||
self
|
||||
}
|
||||
}
|
||||
#[allow(dead_code, non_camel_case_types, missing_docs)]
|
||||
impl<'a, __open: FooPropsBuilder_Optional<Option<&'a str>>> FooPropsBuilder<'a, (__open,)> {
|
||||
pub fn build(self) -> FooProps<'a> {
|
||||
let (open,) = self.fields;
|
||||
let open = FooPropsBuilder_Optional::into_value(open, || Default::default());
|
||||
FooProps {
|
||||
open,
|
||||
attributes: self.attributes,
|
||||
}
|
||||
}
|
||||
}
|
||||
// -----
|
||||
|
||||
impl<'a, A> HasAttributesBox<'a, FooPropsBuilder<'a, (A,)>> for FooPropsBuilder<'a, (A,)> {
|
||||
fn push_attribute(
|
||||
self,
|
||||
name: &'a str,
|
||||
ns: Option<&'static str>,
|
||||
attr: impl IntoAttributeValue<'a>,
|
||||
volatile: bool,
|
||||
) -> Self {
|
||||
let mut attrs = self.attributes;
|
||||
// We insert attributes so that the list is binary-searchable
|
||||
if let Err(index) = attrs.binary_search_by(|probe| probe.name.cmp(name)) {
|
||||
attrs.insert(
|
||||
index,
|
||||
Attribute::new(name, attr.into_value(self.bump), ns, volatile),
|
||||
);
|
||||
}
|
||||
FooPropsBuilder {
|
||||
bump: self.bump,
|
||||
fields: self.fields,
|
||||
attributes: attrs,
|
||||
_phantom: self._phantom,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<A> ExtendedGlobalAttributesMarker for FooPropsBuilder<'_, (A,)> {}
|
||||
impl<A> ExtendedAudioMarker for FooPropsBuilder<'_, (A,)> {}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
pub fn Foo<'a>(cx: Scope<'a, FooProps<'a>>) -> Element<'a> {
|
||||
let muted = false;
|
||||
let attributes = &cx.props.attributes;
|
||||
render! {
|
||||
// rsx! {
|
||||
// audio {
|
||||
// muted: muted,
|
||||
// }
|
||||
// }
|
||||
::dioxus::core::LazyNodes::new(move |__cx: &::dioxus::core::ScopeState| -> ::dioxus::core::VNode {
|
||||
static TEMPLATE: ::dioxus::core::Template = ::dioxus::core::Template { name: concat!(file!(), ":", line!(), ":", column!(), ":", "123" ), roots: &[::dioxus::core::TemplateNode::Element { tag: dioxus_elements::audio::TAG_NAME, namespace: dioxus_elements::audio::NAME_SPACE, attrs: &[::dioxus::core::TemplateAttribute::Dynamic { id: 0usize }], children: &[] }], node_paths: &[], attr_paths: &[&[0u8]] };
|
||||
let mut attrs = vec![__cx.attr(dioxus_elements::audio::muted.0, muted, dioxus_elements::audio::muted.1, dioxus_elements::audio::muted.2)];
|
||||
attrs.push((&**attributes).into());
|
||||
::dioxus::core::VNode {
|
||||
parent: None,
|
||||
key: None,
|
||||
template: std::cell::Cell::new(TEMPLATE),
|
||||
root_ids: Default::default(),
|
||||
dynamic_nodes: __cx.bump().alloc([]),
|
||||
dynamic_attrs: __cx.bump().alloc(attrs),
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -656,12 +656,12 @@ Finally, call `.build()` to create the instance of `{name}`.
|
|||
}
|
||||
}
|
||||
|
||||
impl #impl_generics dioxus::prelude::Properties for #name #ty_generics
|
||||
impl #impl_generics ::dioxus::prelude::Properties<'_> for #name #ty_generics
|
||||
#b_generics_where_extras_predicates
|
||||
{
|
||||
type Builder = #builder_name #generics_with_empty;
|
||||
const IS_STATIC: bool = #is_static;
|
||||
fn builder(_cx: &::dioxus_core::prelude::ScopeState) -> Self::Builder {
|
||||
fn builder(_cx: &::dioxus::prelude::ScopeState) -> Self::Builder {
|
||||
#name::builder()
|
||||
}
|
||||
unsafe fn memoize(&self, other: &Self) -> bool {
|
||||
|
|
|
@ -91,7 +91,7 @@ impl<'a, const A: bool> FragmentBuilder<'a, A> {
|
|||
/// })
|
||||
/// }
|
||||
/// ```
|
||||
impl<'a> Properties for FragmentProps<'a> {
|
||||
impl<'a> Properties<'_> for FragmentProps<'a> {
|
||||
type Builder = FragmentBuilder<'a, false>;
|
||||
const IS_STATIC: bool = false;
|
||||
fn builder(_cx: &ScopeState) -> Self::Builder {
|
||||
|
|
|
@ -896,8 +896,8 @@ impl<'a, T: IntoAttributeValue<'a>> IntoAttributeValue<'a> for Option<T> {
|
|||
pub trait HasAttributesBox<'a, T> {
|
||||
fn push_attribute(
|
||||
self,
|
||||
name: &str,
|
||||
ns: Option<&str>,
|
||||
name: &'a str,
|
||||
ns: Option<&'static str>,
|
||||
attr: impl IntoAttributeValue<'a>,
|
||||
volatile: bool,
|
||||
) -> T;
|
||||
|
|
|
@ -32,7 +32,7 @@ use crate::innerlude::*;
|
|||
/// data: &'a str
|
||||
/// }
|
||||
/// ```
|
||||
pub trait Properties: Sized {
|
||||
pub trait Properties<'a>: Sized {
|
||||
/// The type of the builder for this component.
|
||||
/// Used to create "in-progress" versions of the props.
|
||||
type Builder;
|
||||
|
@ -41,7 +41,7 @@ pub trait Properties: Sized {
|
|||
const IS_STATIC: bool;
|
||||
|
||||
/// Create a builder for this component.
|
||||
fn builder(cx: &ScopeState) -> Self::Builder;
|
||||
fn builder(cx: &'a ScopeState) -> Self::Builder;
|
||||
|
||||
/// Memoization can only happen if the props are valid for the 'static lifetime
|
||||
///
|
||||
|
@ -51,7 +51,7 @@ pub trait Properties: Sized {
|
|||
unsafe fn memoize(&self, other: &Self) -> bool;
|
||||
}
|
||||
|
||||
impl Properties for () {
|
||||
impl Properties<'_> for () {
|
||||
type Builder = EmptyBuilder;
|
||||
const IS_STATIC: bool = true;
|
||||
fn builder(_cx: &ScopeState) -> Self::Builder {
|
||||
|
@ -70,8 +70,8 @@ impl EmptyBuilder {
|
|||
|
||||
/// This utility function launches the builder method so rsx! and html! macros can use the typed-builder pattern
|
||||
/// to initialize a component's props.
|
||||
pub fn fc_to_builder<'a, T: Properties + 'a>(
|
||||
cx: &ScopeState,
|
||||
pub fn fc_to_builder<'a, T: Properties<'a> + 'a>(
|
||||
cx: &'a ScopeState,
|
||||
_: fn(Scope<'a, T>) -> Element<'a>,
|
||||
) -> T::Builder {
|
||||
T::builder(cx)
|
||||
|
|
|
@ -423,7 +423,7 @@ impl<'src> ScopeState {
|
|||
fn_name: &'static str,
|
||||
) -> DynamicNode<'src>
|
||||
where
|
||||
P: Properties + 'child,
|
||||
P: Properties<'child> + 'child,
|
||||
'src: 'child,
|
||||
{
|
||||
let vcomp = VProps::new(component, P::memoize, props);
|
||||
|
|
|
@ -1,187 +0,0 @@
|
|||
use dioxus::core_macro::render;
|
||||
use dioxus::prelude::rsx;
|
||||
use dioxus_core::{Attribute, Element, HasAttributesBox, Scope};
|
||||
use dioxus_html::{ExtendedAudioMarker, ExtendedGlobalAttributesMarker};
|
||||
|
||||
#[test]
|
||||
fn props_spread() {
|
||||
pub struct FooProps<'a> {
|
||||
pub open: Option<&'a str>,
|
||||
attributes: Vec<Attribute<'a>>,
|
||||
}
|
||||
|
||||
// -----
|
||||
impl<'a> FooProps<'a> {
|
||||
#[doc = "\nCreate a builder for building `FooProps`.\nOn the builder, call `.open(...)`(optional) to set the values of the fields.\nFinally, call `.build()` to create the instance of `FooProps`.\n "]
|
||||
#[allow(dead_code)]
|
||||
pub fn builder(cx: &ScopeState) -> FooPropsBuilder<'a, ((),)> {
|
||||
FooPropsBuilder {
|
||||
bump: cx.bump(),
|
||||
fields: ((),),
|
||||
attributes: Vec::new(),
|
||||
_phantom: core::default::Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
#[must_use]
|
||||
#[doc(hidden)]
|
||||
#[allow(dead_code, non_camel_case_types, non_snake_case)]
|
||||
pub struct FooPropsBuilder<'a, TypedBuilderFields> {
|
||||
bump: &'a ::dioxus::core::exports::bumpalo::Bump,
|
||||
fields: TypedBuilderFields,
|
||||
attributes: Vec<Attribute<'a>>,
|
||||
_phantom: (core::marker::PhantomData<&'a ()>),
|
||||
}
|
||||
//impl<'a, TypedBuilderFields, > Clone for FooPropsBuilder<'a, TypedBuilderFields, > where TypedBuilderFields: Clone { fn clone(&self) -> Self { Self { fields: self.fields.clone(), attributes: self.attributes, _phantom: Default::default() } } }
|
||||
impl<'a> dioxus::prelude::Properties for FooProps<'a> {
|
||||
type Builder = FooPropsBuilder<'a, ((),)>;
|
||||
const IS_STATIC: bool = false;
|
||||
fn builder(cx: &ScopeState) -> Self::Builder {
|
||||
FooProps::builder(cx)
|
||||
}
|
||||
unsafe fn memoize(&self, other: &Self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
#[doc(hidden)]
|
||||
#[allow(dead_code, non_camel_case_types, non_snake_case)]
|
||||
pub trait FooPropsBuilder_Optional<T> {
|
||||
fn into_value<F: FnOnce() -> T>(self, default: F) -> T;
|
||||
}
|
||||
impl<T> FooPropsBuilder_Optional<T> for () {
|
||||
fn into_value<F: FnOnce() -> T>(self, default: F) -> T {
|
||||
default()
|
||||
}
|
||||
}
|
||||
impl<T> FooPropsBuilder_Optional<T> for (T,) {
|
||||
fn into_value<F: FnOnce() -> T>(self, _: F) -> T {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
#[allow(dead_code, non_camel_case_types, missing_docs)]
|
||||
impl<'a> FooPropsBuilder<'a, ((),)> {
|
||||
pub fn open(
|
||||
self,
|
||||
open: &'a str,
|
||||
) -> FooPropsBuilder<
|
||||
'a,
|
||||
((
|
||||
Option<&'a str>,
|
||||
// pub attributes: Vec<Attribute<'a>>,
|
||||
),),
|
||||
> {
|
||||
let open = (Some(open),);
|
||||
let (_,) = self.fields;
|
||||
FooPropsBuilder {
|
||||
bump: self.bump,
|
||||
fields: (open,),
|
||||
attributes: self.attributes,
|
||||
_phantom: self._phantom,
|
||||
}
|
||||
}
|
||||
}
|
||||
#[doc(hidden)]
|
||||
#[allow(dead_code, non_camel_case_types, non_snake_case)]
|
||||
pub enum FooPropsBuilder_Error_Repeated_field_open {}
|
||||
#[doc(hidden)]
|
||||
#[allow(dead_code, non_camel_case_types, missing_docs)]
|
||||
impl<'a>
|
||||
FooPropsBuilder<
|
||||
'a,
|
||||
((
|
||||
Option<&'a str>,
|
||||
// pub attributes: Vec<Attribute<'a>>,
|
||||
),),
|
||||
>
|
||||
{
|
||||
#[deprecated(note = "Repeated field open")]
|
||||
pub fn open(
|
||||
self,
|
||||
_: FooPropsBuilder_Error_Repeated_field_open,
|
||||
) -> FooPropsBuilder<
|
||||
'a,
|
||||
((
|
||||
Option<&'a str>,
|
||||
// pub attributes: Vec<Attribute<'a>>,
|
||||
),),
|
||||
> {
|
||||
self
|
||||
}
|
||||
}
|
||||
#[allow(dead_code, non_camel_case_types, missing_docs)]
|
||||
impl<'a, __open: FooPropsBuilder_Optional<Option<&'a str>>> FooPropsBuilder<'a, (__open,)> {
|
||||
pub fn build(self) -> FooProps<'a> {
|
||||
let (open,) = self.fields;
|
||||
let open = FooPropsBuilder_Optional::into_value(open, || Default::default());
|
||||
FooProps {
|
||||
open,
|
||||
attributes: self.attributes,
|
||||
}
|
||||
}
|
||||
}
|
||||
// -----
|
||||
|
||||
impl<'a, A> HasAttributesBox<'a, FooPropsBuilder<'a, (A,)>> for FooPropsBuilder<'a, (A,)> {
|
||||
fn push_attribute(
|
||||
self,
|
||||
name: &str,
|
||||
ns: Option<&str>,
|
||||
attr: impl IntoAttributeValue<'a>,
|
||||
volatile: bool,
|
||||
) -> Self {
|
||||
let mut attrs = Vec::from(self.attributes);
|
||||
attrs.push(Attribute::new(
|
||||
name,
|
||||
attr.into_value(self.bump),
|
||||
ns,
|
||||
volatile,
|
||||
));
|
||||
FooPropsBuilder {
|
||||
bump: self.bump,
|
||||
fields: self.fields,
|
||||
attributes: attrs,
|
||||
_phantom: self._phantom,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<A> ExtendedGlobalAttributesMarker for FooPropsBuilder<'_, (A,)> {}
|
||||
impl<A> ExtendedAudioMarker for FooPropsBuilder<'_, (A,)> {}
|
||||
|
||||
use dioxus::prelude::*;
|
||||
use dioxus_html::AudioExtension;
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
pub fn Foo<'a>(cx: Scope<'a, FooProps<'a>>) -> Element<'a> {
|
||||
let muted = false;
|
||||
let attributes = &cx.props.attributes;
|
||||
render! {
|
||||
// rsx! {
|
||||
// audio {
|
||||
// muted: muted,
|
||||
// }
|
||||
// }
|
||||
::dioxus::core::LazyNodes::new(move |__cx: &::dioxus::core::ScopeState| -> ::dioxus::core::VNode {
|
||||
static TEMPLATE: ::dioxus::core::Template = ::dioxus::core::Template { name: concat!(file!(), ":", line!(), ":", column!(), ":", "" ), roots: &[::dioxus::core::TemplateNode::Element { tag: dioxus_elements::audio::TAG_NAME, namespace: dioxus_elements::audio::NAME_SPACE, attrs: &[::dioxus::core::TemplateAttribute::Dynamic { id: 0usize }], children: &[] }], node_paths: &[], attr_paths: &[&[0u8]] };
|
||||
let mut attrs = vec![__cx.attr(dioxus_elements::audio::muted.0, muted, dioxus_elements::audio::muted.1, dioxus_elements::audio::muted.2)];
|
||||
for attr in attributes {
|
||||
attrs.push(attr);
|
||||
}
|
||||
::dioxus::core::VNode {
|
||||
parent: None,
|
||||
key: None,
|
||||
template: std::cell::Cell::new(TEMPLATE),
|
||||
root_ids: Default::default(),
|
||||
dynamic_nodes: __cx.bump().alloc([]),
|
||||
dynamic_attrs: __cx.bump().alloc(attrs),
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
rsx! {
|
||||
Foo {
|
||||
autoplay: true,
|
||||
controls: true,
|
||||
}
|
||||
};
|
||||
}
|
Loading…
Reference in a new issue