浏览代码

fix extending an element

Evan Almloff 1 年之前
父节点
当前提交
dc446b5e5b

+ 1 - 1
packages/core-macro/src/props/mod.rs

@@ -661,7 +661,7 @@ Finally, call `.build()` to create the instance of `{name}`.
                 {
                     type Builder = #builder_name #generics_with_empty;
                     const IS_STATIC: bool = #is_static;
-                    fn builder() -> Self::Builder {
+                    fn builder(_cx: &::dioxus_core::prelude::ScopeState) -> Self::Builder {
                         #name::builder()
                     }
                     unsafe fn memoize(&self, other: &Self) -> bool {

+ 1 - 1
packages/core/src/fragment.rs

@@ -94,7 +94,7 @@ impl<'a, const A: bool> FragmentBuilder<'a, A> {
 impl<'a> Properties for FragmentProps<'a> {
     type Builder = FragmentBuilder<'a, false>;
     const IS_STATIC: bool = false;
-    fn builder() -> Self::Builder {
+    fn builder(_cx: &ScopeState) -> Self::Builder {
         FragmentBuilder(None)
     }
     unsafe fn memoize(&self, _other: &Self) -> bool {

+ 5 - 5
packages/core/src/lib.rs

@@ -73,11 +73,11 @@ pub(crate) mod innerlude {
 }
 
 pub use crate::innerlude::{
-    fc_to_builder, vdom_is_rendering, AnyValue, Attribute, AttributeBox, AttributeValue, BorrowedAttributeValue,
-    CapturedError, Component, DynamicNode, Element, ElementId, Event, Fragment, HasAttributesBox, IntoDynNode,
-    LazyNodes, Mutation, Mutations, Properties, RenderReturn, Scope, ScopeId, ScopeState, Scoped,
-    TaskId, Template, TemplateAttribute, TemplateNode, VComponent, VNode, VPlaceholder, VText,
-    VirtualDom,
+    fc_to_builder, vdom_is_rendering, AnyValue, Attribute, AttributeValue, BorrowedAttributeValue,
+    CapturedError, Component, DynamicNode, Element, ElementId, Event, Fragment, HasAttributesBox,
+    IntoDynNode, LazyNodes, Mutation, Mutations, Properties, RenderReturn, Scope, ScopeId,
+    ScopeState, Scoped, TaskId, Template, TemplateAttribute, TemplateNode, VComponent, VNode,
+    VPlaceholder, VText, VirtualDom,
 };
 
 /// The purpose of this module is to alleviate imports of many common types

+ 7 - 28
packages/core/src/nodes.rs

@@ -833,33 +833,12 @@ impl<'a, T: IntoAttributeValue<'a>> IntoAttributeValue<'a> for Option<T> {
     }
 }
 
-pub struct AttributeBox<'a> {
-    /// The name of the attribute.
-    pub name: &'a str,
-
-    /// The value of the attribute
-    pub value: Box<dyn IntoAttributeValue<'a> + 'static>,
-
-    /// The namespace of the attribute.
-    ///
-    /// Doesn’t exist in the html spec. Used in Dioxus to denote “style” tags and other attribute groups.
-    pub namespace: Option<&'static str>,
-
-    /// An indication of we should always try and set the attribute. Used in controlled components to ensure changes are propagated
-    pub volatile: bool,
-}
-
-impl<'a> AttributeBox<'a> {
-    pub fn new(name: &'a str, value: impl IntoAttributeValue<'a> + 'static, namespace: Option<&'static str>, volatile: bool) -> Self {
-        Self {
-            name,
-            value: Box::new(value),
-            namespace,
-            volatile,
-        }
-    }
-}
-
 pub trait HasAttributesBox<'a, T> {
-    fn push_attribute(self, attr: AttributeBox<'a>) -> T;
+    fn push_attribute(
+        self,
+        name: &str,
+        ns: Option<&str>,
+        attr: impl IntoAttributeValue<'a>,
+        volatile: bool,
+    ) -> T;
 }

+ 7 - 4
packages/core/src/properties.rs

@@ -41,7 +41,7 @@ pub trait Properties: Sized {
     const IS_STATIC: bool;
 
     /// Create a builder for this component.
-    fn builder() -> Self::Builder;
+    fn builder(cx: &ScopeState) -> Self::Builder;
 
     /// Memoization can only happen if the props are valid for the 'static lifetime
     ///
@@ -54,7 +54,7 @@ pub trait Properties: Sized {
 impl Properties for () {
     type Builder = EmptyBuilder;
     const IS_STATIC: bool = true;
-    fn builder() -> Self::Builder {
+    fn builder(_cx: &ScopeState) -> Self::Builder {
         EmptyBuilder {}
     }
     unsafe fn memoize(&self, _other: &Self) -> bool {
@@ -70,8 +70,11 @@ 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>(_: fn(Scope<'a, T>) -> Element<'a>) -> T::Builder {
-    T::builder()
+pub fn fc_to_builder<'a, T: Properties + 'a>(
+    cx: &ScopeState,
+    _: fn(Scope<'a, T>) -> Element<'a>,
+) -> T::Builder {
+    T::builder(cx)
 }
 
 #[cfg(not(miri))]

+ 104 - 38
packages/dioxus/tests/props_spread.rs

@@ -1,51 +1,83 @@
 use dioxus::core_macro::render;
 use dioxus::prelude::rsx;
-use dioxus_core::{AttributeBox, Element, HasAttributesBox, Scope};
+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<AttributeBox<'a>>,
+        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() -> FooPropsBuilder<'a, ((), ), > {
-            FooPropsBuilder { fields: ((), ), attributes: Vec::new(), _phantom: core::default::Default::default() }
+        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, > {
+    pub struct FooPropsBuilder<'a, TypedBuilderFields> {
+        bump: &'a ::dioxus::core::exports::bumpalo::Bump,
         fields: TypedBuilderFields,
-        attributes: Vec<AttributeBox<'a>>,
-        _phantom: ( core::marker::PhantomData<&'a ()>   ),
+        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, ((), ), >;
+        type Builder = FooPropsBuilder<'a, ((),)>;
         const IS_STATIC: bool = false;
-        fn builder() -> Self::Builder { FooProps::builder() }
-        unsafe fn memoize(&self, other: &Self) -> 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 } }
+    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 { fields: (open, ), attributes: self.attributes, _phantom: self._phantom }
+    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)]
@@ -53,33 +85,67 @@ fn props_spread() {
     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>>,
-                                  ), )> {
+    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 }
+        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, ), > {
+    impl<'a, __open: FooPropsBuilder_Optional<Option<&'a str>>> FooPropsBuilder<'a, (__open,)> {
         pub fn build(self) -> FooProps<'a> {
-            let (open, ) = self.fields;
+            let (open,) = self.fields;
             let open = FooPropsBuilder_Optional::into_value(open, || Default::default());
-            FooProps { open, attributes: self.attributes }
+            FooProps {
+                open,
+                attributes: self.attributes,
+            }
         }
     }
     // -----
 
-    impl<'a, A> HasAttributesBox<'a, FooPropsBuilder<'a, (A, )>> for FooPropsBuilder<'a, (A, )> {
-        fn push_attribute(self, attr: AttributeBox<'a>) -> FooPropsBuilder<'a, (A, )> {
+    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(attr);
-            FooPropsBuilder { fields: self.fields, attributes: attrs, _phantom: self._phantom }
+            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,)> {}
+    impl<A> ExtendedGlobalAttributesMarker for FooPropsBuilder<'_, (A,)> {}
+    impl<A> ExtendedAudioMarker for FooPropsBuilder<'_, (A,)> {}
 
     use dioxus::prelude::*;
     use dioxus_html::AudioExtension;
@@ -87,7 +153,7 @@ fn props_spread() {
     #[allow(non_snake_case)]
     pub fn Foo<'a>(cx: Scope<'a, FooProps<'a>>) -> Element<'a> {
         let muted = false;
-        let attributes = cx.props.attributes;
+        let attributes = &cx.props.attributes;
         render! {
             // rsx! {
             //     audio {
@@ -99,7 +165,7 @@ fn props_spread() {
                 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(__cx.attr(attr.name, attr.value.into_value(__cx.bump()), attr.namespace, attr.volatile));
-                };
+                }
                 ::dioxus::core::VNode {
                     parent: None,
                     key: None,
@@ -118,4 +184,4 @@ fn props_spread() {
             controls: true,
         }
     };
-}
+}

+ 9 - 6
packages/html-internal-macro/src/lib.rs

@@ -1,11 +1,11 @@
 use proc_macro::TokenStream;
 
 use convert_case::{Case, Casing};
-use quote::{quote, TokenStreamExt, ToTokens};
-use syn::{braced, Ident, parse_macro_input, Token};
+use quote::{quote, ToTokens, TokenStreamExt};
 use syn::__private::TokenStream2;
 use syn::parse::{Parse, ParseStream};
 use syn::punctuated::Punctuated;
+use syn::{braced, parse_macro_input, Ident, Token};
 
 #[proc_macro]
 pub fn impl_extension_attributes(input: TokenStream) -> TokenStream {
@@ -42,11 +42,14 @@ impl ToTokens for ImplExtensionAttributes {
         let camel_name = name.to_string().to_case(Case::UpperCamel);
         let impl_name = Ident::new(format!("{}Impl", &camel_name).as_str(), name.span());
         let extension_name = Ident::new(format!("{}Extension", &camel_name).as_str(), name.span());
-        let marker_name = Ident::new(format!("Extended{}Marker", &camel_name).as_str(), name.span());
+        let marker_name = Ident::new(
+            format!("Extended{}Marker", &camel_name).as_str(),
+            name.span(),
+        );
 
         if !self.is_element {
             tokens.append_all(quote! {
-                trait #impl_name {}
+                struct #impl_name;
                 impl #name for #impl_name {}
             });
         }
@@ -60,12 +63,12 @@ impl ToTokens for ImplExtensionAttributes {
             let d = if self.is_element {
                 quote! { #name::#ident }
             } else {
-                quote! { #impl_name::#ident }
+                quote! { <#impl_name as #name>::#ident }
             };
             quote! {
                 fn #ident(self, value: impl IntoAttributeValue<'a> + 'static) -> Self {
                     let d = #d;
-                    self.push_attribute(AttributeBox::new(d.0, value, d.1, d.2))
+                    self.push_attribute(d.0, d.1, value, d.2)
                 }
             }
         });

+ 1 - 2
packages/html/src/elements.rs

@@ -1,8 +1,7 @@
 #![allow(non_upper_case_globals)]
 
-use dioxus_core::AttributeBox;
-use dioxus_core::HasAttributesBox;
 use dioxus_core::prelude::IntoAttributeValue;
+use dioxus_core::HasAttributesBox;
 use dioxus_html_internal_macro::impl_extension_attributes;
 #[cfg(feature = "hot-reload-context")]
 use dioxus_rsx::HotReloadingContext;

+ 1 - 2
packages/html/src/global_attributes.rs

@@ -1,8 +1,7 @@
 #![allow(non_upper_case_globals)]
 
-use dioxus_core::HasAttributesBox;
-use dioxus_core::AttributeBox;
 use dioxus_core::prelude::IntoAttributeValue;
+use dioxus_core::HasAttributesBox;
 use dioxus_html_internal_macro::impl_extension_attributes;
 
 use crate::AttributeDiscription;

+ 2 - 2
packages/rsx/src/component.rs

@@ -151,8 +151,8 @@ impl ToTokens for Component {
             }
             None => {
                 let mut toks = match prop_gen_args {
-                    Some(gen_args) => quote! { fc_to_builder(#name #gen_args) },
-                    None => quote! { fc_to_builder(#name) },
+                    Some(gen_args) => quote! { fc_to_builder(__cx, #name #gen_args) },
+                    None => quote! { fc_to_builder(__cx, #name) },
                 };
                 for field in &self.fields {
                     match field.name.to_string().as_str() {