diff --git a/README.md b/README.md index 1e59973f8..cb9106aa0 100644 --- a/README.md +++ b/README.md @@ -170,10 +170,10 @@ Dioxus is heavily inspired by React, but we want your transition to feel like an | Suspense | 🛠 | ✅ | schedule future render from future/promise | | Cooperative Scheduling | 🛠 | ✅ | Prioritize important events over non-important events | | Fine-grained reactivity | 🛠 | ❓ | Skip diffing for fine-grain updates | -| Compile-time correct | ✅ | ❓ | Throw errors on invalid template layouts | | Runs natively | ✅ | ❓ | runs as a portable binary w/o a runtime (Node) | | 1st class global state | ✅ | ❓ | redux/recoil/mobx on top of context | | Subtree Memoization | ✅ | ❓ | skip diffing static element subtrees | +| Compile-time correct | ✅ | ❓ | Throw errors on invalid template layouts | | Heuristic Engine | 🛠 | ❓ | track component memory usage to minimize future allocations | | NodeRef | 🛠 | ✅ | gain direct access to nodes [1] | diff --git a/examples/rsx_usage.rs b/examples/rsx_usage.rs index da5eef235..ba5ec951e 100644 --- a/examples/rsx_usage.rs +++ b/examples/rsx_usage.rs @@ -89,7 +89,7 @@ static Example: FC<()> = |cx| { // Expressions can be used in element position too: {rsx!(p { "More templating!" })} - {html!(
"Even HTML templating!!"
)} + // {html!("Even HTML templating!!"
)} // Iterators {(0..10).map(|i| rsx!(li { "{i}" }))} diff --git a/examples/testbed.rs b/examples/testbed.rs index 1d03ac7b4..240c75a76 100644 --- a/examples/testbed.rs +++ b/examples/testbed.rs @@ -2,7 +2,7 @@ use std::cell::Cell; use dioxus::prelude::*; use dioxus_core::{ - nodes::{NodeKey, VElement, VText}, + nodes::{VElement, VText}, RealDomNode, }; @@ -23,6 +23,9 @@ const Example: FC<()> = |cx| { Fragment { Fragment { "h1" + div { + + } } "h2" } diff --git a/packages/core-macro/src/rsx/component.rs b/packages/core-macro/src/rsx/component.rs index 72e239514..e24d8c98b 100644 --- a/packages/core-macro/src/rsx/component.rs +++ b/packages/core-macro/src/rsx/component.rs @@ -188,7 +188,7 @@ impl ToTokens for Component { #name, #builder, #key_token, - #children + __cx.bump().alloc(#children) ) }) } diff --git a/packages/core-macro/src/rsx/element.rs b/packages/core-macro/src/rsx/element.rs index a08fa129b..222d6fb18 100644 --- a/packages/core-macro/src/rsx/element.rs +++ b/packages/core-macro/src/rsx/element.rs @@ -23,37 +23,18 @@ pub struct Element { impl ToTokens for Element { fn to_tokens(&self, tokens: &mut TokenStream2) { let name = &self.name; + let attr = &self.attributes; + let childs = &self.children; + let listeners = &self.listeners; tokens.append_all(quote! { - __cx.element(dioxus_elements::#name) - }); - - // By gating these methods, we can keep the output of `cargo expand` readable. - // We also prevent the issue where zero-sized arrays fail to have propery type inference. - - if self.attributes.len() > 0 { - let attr = &self.attributes; - tokens.append_all(quote! { - .attributes([ #(#attr),* ]) - }) - } - - if self.children.len() > 0 { - let childs = &self.children; - tokens.append_all(quote! { - .children([ #(#childs),* ]) - }); - } - - if self.listeners.len() > 0 { - let listeners = &self.listeners; - tokens.append_all(quote! { - .listeners([ #(#listeners),* ]) - }); - } - - tokens.append_all(quote! { - .finish() + __cx.element( + dioxus_elements::#name, + __cx.bump().alloc([ #(#listeners),* ]), + __cx.bump().alloc([ #(#attr),* ]), + __cx.bump().alloc([ #(#childs),* ]), + None, + ) }); } } @@ -83,7 +64,13 @@ impl Parse for Element { } if content.peek(Ident) && content.peek2(Token![:]) && !content.peek3(Token![:]) { - parse_element_body(&content, &mut attributes, &mut listeners, &mut key)?; + parse_element_body( + &content, + &mut attributes, + &mut listeners, + &mut key, + name.clone(), + )?; } else { children.push(content.parse::( + pub fn virtual_child
( &self, component: FC
, props: P, diff --git a/packages/html/src/lib.rs b/packages/html/src/lib.rs index 4bd425c0f..d10ba230b 100644 --- a/packages/html/src/lib.rs +++ b/packages/html/src/lib.rs @@ -19,48 +19,213 @@ use std::fmt::Arguments; use dioxus_core::{nodes::Attribute, DioxusElement, NodeFactory}; -trait GlobalAttributes { - fn accesskey<'a>(&self, cx: NodeFactory<'a>, val: Arguments) -> Attribute<'a> { - cx.attr("accesskey", val, None, false) +macro_rules! no_namespace_trait_methods { + ( + $( + $(#[$attr:meta])* + $name:ident; + )* + ) => { + $( + $(#[$attr])* + fn $name<'a>(&self, cx: NodeFactory<'a>, val: Arguments) -> Attribute<'a> { + cx.attr(stringify!(name), val, None, false) + } + )* + }; +} +macro_rules! style_trait_methods { + ( + $( + $(#[$attr:meta])* + $name:ident: $lit:literal, + )* + ) => { + $( + $(#[$attr])* + fn $name<'a>(&self, cx: NodeFactory<'a>, val: Arguments) -> Attribute<'a> { + cx.attr($lit, val, Some("style"), false) + } + )* + }; +} +macro_rules! aria_trait_methods { + ( + $( + $(#[$attr:meta])* + $name:ident: $lit:literal, + )* + ) => { + $( + $(#[$attr])* + fn $name<'a>(&self, cx: NodeFactory<'a>, val: Arguments) -> Attribute<'a> { + cx.attr($lit, val, None, false) + } + )* + }; +} + +pub trait GlobalAttributes { + no_namespace_trait_methods! { + accesskey; + class; + contenteditable; + data; + dir; + draggable; + hidden; + id; + lang; + spellcheck; + style; + tabindex; + title; + translate; } - fn class<'a>(&self, cx: NodeFactory<'a>, val: Arguments<'a>) -> Attribute<'a> { - cx.attr("class", val, None, false) + style_trait_methods! { + background: "background", + background_attachment: "background-attachment", + + /// ## Definition and Usage + /// + /// The background-color property sets the background color of an element. + /// + /// The background of an element is the total size of the element, including padding and border (but not the margin). + /// + /// Tip: Use a background color and a text color that makes the text easy to read. + /// + /// ## Example + /// + /// ``` + /// body { + /// style: { + /// background_color: "coral" + /// } + /// } + /// ``` + background_color: "background-color", + background_image: "background-image", + background_position: "background-position", + background_repeat: "background-repeat", + border: "border", + border_bottom: "border-bottom", + border_bottom_color: "border-bottom-color", + border_bottom_style: "border-bottom-style", + border_bottom_width: "border-bottom-width", + border_color: "border-color", + border_left: "border-left", + border_left_color: "border-left-color", + border_left_style: "border-left-style", + border_left_width: "border-left-width", + border_right: "border-right", + border_right_color: "border-right-color", + border_right_style: "border-right-style", + border_right_width: "border-right-width", + border_style: "border-style", + border_top: "border-top", + border_top_color: "border-top-color", + border_top_style: "border-top-style", + border_top_width: "border-top-width", + border_width: "border-width", + clear: "clear", + clip: "clip", + color: "color", + cursor: "cursor", + display: "display", + filter: "filter", + css_float: "css-float", + font: "font", + font_family: "font-family", + font_size: "font-size", + font_variant: "font-variant", + font_weight: "font-weight", + height: "height", + left: "left", + letter_spacing: "letter-spacing", + line_height: "line-height", + list_style: "list-style", + list_style_image: "list-style-image", + list_style_position: "list-style-position", + list_style_type: "list-style-type", + margin: "margin", + margin_bottom: "margin-bottom", + margin_left: "margin-left", + margin_right: "margin-right", + margin_top: "margin-top", + overflow: "overflow", + padding: "padding", + padding_bottom: "padding-bottom", + padding_left: "padding-left", + padding_right: "padding-right", + padding_top: "padding-top", + page_break_after: "page-break-after", + page_break_before: "page-break-before", + position: "position", + stroke_dasharray: "stroke-dasharray", + stroke_dashoffset: "stroke-dashoffset", + text_align: "text-align", + text_decoration: "text-decoration", + text_indent: "text-indent", + text_transform: "text-transform", + top: "top", + vertical_align: "vertical-align", + visibility: "visibility", + width: "width", + z_index: "z-index", } - fn contenteditable<'a>(&self, cx: NodeFactory<'a>, val: Arguments<'a>) -> Attribute<'a> { - cx.attr("contenteditable", val, None, false) - } - fn data<'a>(&self, cx: NodeFactory<'a>, val: Arguments<'a>) -> Attribute<'a> { - cx.attr("data", val, None, false) - } - fn dir<'a>(&self, cx: NodeFactory<'a>, val: Arguments<'a>) -> Attribute<'a> { - cx.attr("dir", val, None, false) - } - fn draggable<'a>(&self, cx: NodeFactory<'a>, val: Arguments<'a>) -> Attribute<'a> { - cx.attr("draggable", val, None, false) - } - fn hidden<'a>(&self, cx: NodeFactory<'a>, val: Arguments<'a>) -> Attribute<'a> { - cx.attr("hidden", val, None, false) - } - fn id<'a>(&self, cx: NodeFactory<'a>, val: Arguments<'a>) -> Attribute<'a> { - cx.attr("id", val, None, false) - } - fn lang<'a>(&self, cx: NodeFactory<'a>, val: Arguments<'a>) -> Attribute<'a> { - cx.attr("lang", val, None, false) - } - fn spellcheck<'a>(&self, cx: NodeFactory<'a>, val: Arguments<'a>) -> Attribute<'a> { - cx.attr("spellcheck", val, None, false) - } - fn style<'a>(&self, cx: NodeFactory<'a>, val: Arguments<'a>) -> Attribute<'a> { - cx.attr("style", val, Some("style"), false) - } - fn tabindex<'a>(&self, cx: NodeFactory<'a>, val: Arguments<'a>) -> Attribute<'a> { - cx.attr("tabindex", val, None, false) - } - fn title<'a>(&self, cx: NodeFactory<'a>, val: Arguments<'a>) -> Attribute<'a> { - cx.attr("title", val, None, false) - } - fn translate<'a>(&self, cx: NodeFactory<'a>, val: Arguments<'a>) -> Attribute<'a> { - cx.attr("translate", val, None, false) + aria_trait_methods! { + aria_current: "aria-current", + aria_details: "aria-details", + aria_disabled: "aria-disabled", + aria_hidden: "aria-hidden", + aria_invalid: "aria-invalid", + aria_keyshortcuts: "aria-keyshortcuts", + aria_label: "aria-label", + aria_roledescription: "aria-roledescription", + // Widget Attributes + aria_autocomplete: "aria-autocomplete", + aria_checked: "aria-checked", + aria_expanded: "aria-expanded", + aria_haspopup: "aria-haspopup", + aria_level: "aria-level", + aria_modal: "aria-modal", + aria_multiline: "aria-multiline", + aria_multiselectable: "aria-multiselectable", + aria_orientation: "aria-orientation", + aria_placeholder: "aria-placeholder", + aria_pressed: "aria-pressed", + aria_readonly: "aria-readonly", + aria_required: "aria-required", + aria_selected: "aria-selected", + aria_sort: "aria-sort", + aria_valuemax: "aria-valuemax", + aria_valuemin: "aria-valuemin", + aria_valuenow: "aria-valuenow", + aria_valuetext: "aria-valuetext", + // Live Region Attributes + aria_atomic: "aria-atomic", + aria_busy: "aria-busy", + aria_live: "aria-live", + aria_relevant: "aria-relevant", + + aria_dropeffect: "aria-dropeffect", + aria_grabbed: "aria-grabbed", + // Relationship Attributes + aria_activedescendant: "aria-activedescendant", + aria_colcount: "aria-colcount", + aria_colindex: "aria-colindex", + aria_colspan: "aria-colspan", + aria_controls: "aria-controls", + aria_describedby: "aria-describedby", + aria_errormessage: "aria-errormessage", + aria_flowto: "aria-flowto", + aria_labelledby: "aria-labelledby", + aria_owns: "aria-owns", + aria_posinset: "aria-posinset", + aria_rowcount: "aria-rowcount", + aria_rowindex: "aria-rowindex", + aria_rowspan: "aria-rowspan", + aria_setsize: "aria-setsize", } } @@ -87,7 +252,7 @@ macro_rules! builder_constructors { impl $name { $( - pub fn $fil<'a>(&self, cx: NodeFactory<'a>, val: Arguments<'a>) -> Attribute<'a> { + pub fn $fil<'a>(&self, cx: NodeFactory<'a>, val: Arguments) -> Attribute<'a> { cx.attr(stringify!($fil), val, None, false) } )* @@ -149,7 +314,7 @@ builder_constructors! { rel: LinkType, sizes: String, // FIXME title: String, // FIXME - // type: Mime, + r#type: Mime, }; /// Build a @@ -166,7 +331,7 @@ builder_constructors! { /// [`