This commit is contained in:
Jonathan Kelley 2021-07-13 15:34:12 -04:00
parent 4091846934
commit 996247a164
7 changed files with 171 additions and 122 deletions

View file

@ -41,10 +41,6 @@
<a href="https://docs.rs/async-imap">
Examples
</a>
<span> | </span>
<a href="https://github.com/async-email/async-imap/releases">
Releases
</a>
</h3>
</div>

View file

@ -50,3 +50,24 @@ div {
## Subtree memoization
The rsx! macro needs to be *really* smart. If it detects that no dynamics are pumped into the macro, then it opts to use the "const" flavors of the element build functions we know and love. This has to be done at build time rather than runtime since components may return basically anything. Using the const flavor enables is_static which encourages Dioxus to do a ptr compare instead of a value compare to short circuit through the diffing. Due to const folding in Rust, entire subtrees can be ruled out at compile time.
It would be interesting to fix the issue of dynamic subtrees by hashing each structure (or just the const structures) or the macro call itself. That way, each call gets its own identifier and we can make sure that two unique structures have different IDs and aren't just opaque to dioxus.
```rust
let s1 = LazyNodes::new("1", move |_| {
if rand() {
f.element()
} else {
f.element()
}
});
let s2 = LazyNodes::new("1", move |f| {
if rand() {
f.element()
} else {
f.element()
}
});
// produces the same ID with different structures
// perhaps just make this
```

View file

@ -1379,3 +1379,13 @@ impl<'a> Iterator for RealChildIterator<'a> {
returned_node
}
}
fn compare_strs(a: &str, b: &str) -> bool {
// Check by pointer, optimizing for static strs
if !std::ptr::eq(a, b) {
// If the pointers are different then check by value
a == b
} else {
true
}
}

View file

@ -455,8 +455,32 @@ impl Debug for VNode<'_> {
mod tests {
use super::*;
static B1: &str = "hello world!";
static B2: &str = "hello world!";
#[test]
fn test() {}
fn test() {
dbg!("Hello world!" as *const _ as *const ());
dbg!("Hello world!" as *const _ as *const ());
// dbg!(B1 as *const _ as *const ());
// dbg!(B2 as *const _ as *const ());
// goal: elements as const
// let b = A.clone();
// A.dom_id.set(RealDomNode::new(10));
// let p = &A;
// p.dom_id.set(RealDomNode::new(10));
// dbg!(p.dom_id.get());
// dbg!(p as *const _ as *const ());
// dbg!(&A as *const _ as *const ());
// // dbg!(b.dom_id.get());
// dbg!(A.dom_id.get());
// A.dom_id.set(RealDomNode::empty());
// let g = A.dom_id.get();
}
#[test]
fn sizing() {

View file

@ -1,6 +1,6 @@
# Html (and SVG) Namespace for Dioxus
The Dioxus `rsx!` and `html!` macros can accept any compile-time correct namespace on top of NodeBuilder. This crate provides the HTML (and SVG) namespaces which get imported in the Dioxus prelude.
The Dioxus `rsx!` and `html!` macros can accept any compile-time correct namespace on top of NodeFactory. This crate provides the HTML (and SVG) namespaces which get imported in the Dioxus prelude.
However, this abstraction enables you to add any namespace of elements, provided they're in scope when rsx! is called. For an example, a UI that is designed for Augmented Reality might use different primitives than HTML:
@ -20,3 +20,88 @@ rsx! {
```
This is currently a not-very-explored part of Dioxus. However, the namespacing system does make it possible to provide syntax highlighting, documentation, "go to definition" and compile-time correctness, so it's worth having it abstracted.
## How it works:
Elements for dioxus must implement the (simple) DioxusElement trait to be used in the rsx! macro.
```rust
struct div;
impl DioxusElement for div {
const TAG_NAME: &'static str = "div";
const NAME_SPACE: Option<&'static str> = None;
}
```
All elements should be defined as a zero-sized-struct (also known as unit struct). These structs are zero-cost and just provide the type-level trickery to Rust for compile-time correct templates.
Attributes would then be implemented as methods on these unit structs.
The HTML namespace is defined mostly with macros. However, the expanded form would look something like this:
```rust
struct base;
impl DioxusElement for base {
const TAG_NAME: &'static str = "base";
const NAME_SPACE: Option<&'static str> = None;
}
impl base {
#[inline]
fn href<'a>(&self, f: NodeFactory<'a>, v: Arguments) -> Attribute<'a> {
f.attr("href", v, None, false)
}
#[inline]
fn target<'a>(&self, f: NodeFactory<'a>, v: Arguments) -> Attribute<'a> {
f.attr("target", v, None, false)
}
}
```
Because attributes are defined as methods on the unit struct, they guard the attribute creation behind a compile-time correct interface.
## How to extend it:
Whenever the rsx! macro is called, it relies on a module `dioxus_elements` to be in scope. When you enable the `html` feature in dioxus, this module gets imported in the prelude. However, you can extend this with your own set of custom elements by making your own `dioxus_elements` module and re-exporting the html namespace.
```rust
mod dioxus_elements {
use dioxus::prelude::dioxus_elements::*;
struct my_element;
impl DioxusElement for my_element {
const TAG_NAME: &'static str = "base";
const NAME_SPACE: Option<&'static str> = None;
}
}
```
## Limitations:
-
## How to work around it:
If an attribute in Dioxus is invalid (defined incorrectly) - first, make an issue - but then, you can work around it. The raw builder API is actually somewhat ergonomic to work with, and the NodeFactory type exposes a bunch of methods to make any type of tree - even invalid ones! So obviously, be careful, but there's basically anything you can do.
```rust
cx.render(rsx!{
div {
h1 {}
// Oh no! I need a super custom element
{LazyNodes::new(move |f| {
f.raw_element(
// tag name
"custom_element",
// attributes
&[f.attr("billy", format_args!("goat"))],
// listeners
&[f.listener(onclick(move |_| {}))],
// children
&[cx.render(rsx!(div {} ))],
// key
None
)
})}
}
})
```

View file

@ -68,6 +68,35 @@ macro_rules! aria_trait_methods {
pub trait GlobalAttributes {
no_namespace_trait_methods! {
accesskey;
/// The HTML class attribute is used to specify a class for an HTML element.
///
/// ## Details
/// Multiple HTML elements can share the same class.
///
/// The class global attribute is a space-separated list of the case-sensitive classes of the element.
/// Classes allow CSS and Javascript to select and access specific elements via the class selectors or
/// functions like the DOM method document.getElementsByClassName.
///
/// ## Example
///
/// ### HTML:
/// ```html
/// <p class="note editorial">Above point sounds a bit obvious. Remove/rewrite?</p>
/// ```
///
/// ### CSS:
/// ```css
/// .note {
/// font-style: italic;
/// font-weight: bold;
/// }
///
/// .editorial {
/// background: rgb(255, 0, 0, .25);
/// padding: 10px;
/// }
/// ```
class;
contenteditable;
data;

View file

@ -1,116 +0,0 @@
//! Dedicated styling system for Components
//! ---------------------------------------
//!
//! In the future, we'd like to move this out of Dioxus core or build a better, more general abstraction. For now, dedicated
//! styling is more-or-less hardcoded into Dioxus.
//!
//!
//!
//!
//!
//!
//!
//!
use crate::innerlude::{Attribute, NodeFactory};
pub struct StyleBuilder;
pub trait AsAttr<'a> {
fn to_attr(self, field: &'static str, fac: &NodeFactory<'a>) -> Attribute<'a>;
}
impl<'a> AsAttr<'a> for std::fmt::Arguments<'a> {
fn to_attr(self, field: &'static str, fac: &NodeFactory<'a>) -> Attribute<'a> {
fac.attr(field, self, Some("style"))
}
}
macro_rules! build_styles {
($ ($name:ident: $lit:literal,)* ) => {
impl StyleBuilder {
$(
pub fn $name<'a>(f: &NodeFactory<'a>, args: impl AsAttr<'a>) -> Attribute<'a> {
args.to_attr($lit, f)
}
)*
}
};
}
build_styles! {
background: "background",
background_attachment: "background-attachment",
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 example(f: &NodeFactory) {
let style_list = &[("width", "10"), ("text-decoration", "")];
let styles = &[StyleBuilder::background(f, format_args!("10"))];
}