wip: fix issues with lifetimes

this commit fixes some lifetime issues regarding having to "move" data into the IntoVNode closure.
This commit is contained in:
Jonathan Kelley 2021-07-07 13:51:55 -04:00
parent 22e659c2bd
commit a38a81e129
39 changed files with 566 additions and 255 deletions

View file

@ -107,29 +107,29 @@ Dioxus is heavily inspired by React, but we want your transition to feel like an
| Feature | Dioxus | React | Notes for Dioxus |
| ----------------------- | ------ | ----- | ----------------------------------------------------- |
| Conditional Rendering | ✅ | ✅ | if/then to hide/show component |
| Map, Iterator | ✅ | ✅ | map/filter/reduce rsx! |
| Map, Iterator | ✅ | ✅ | map/filter/reduce to produce rsx! |
| Keyed Components | ✅ | ✅ | advanced diffing with keys |
| Web | ✅ | ✅ | renderer for web browser |
| Desktop (webview) | ✅ | ✅ | renderer for desktop |
| Context | ✅ | ✅ | share state through the tree |
| Shared State (Context) | ✅ | ✅ | share state through the tree |
| Hook | ✅ | ✅ | memory cells in components |
| SSR | ✅ | ✅ | render directly to string |
| Component Children | ✅ | ✅ | cx.children() as a list of nodes |
| Null components | ✅ | ✅ | allow returning no components |
| No-div components | ✅ | ✅ | components that render components |
| Fragments | ✅ | ✅ | rsx! can return multiple elements without a root |
| Headless components | ✅ | ✅ | components that don't return real elements |
| Fragments | ✅ | ✅ | multiple elements without a real root |
| Manual Props | ✅ | ✅ | Manually pass in props with spread syntax |
| Controlled Inputs | ✅ | ✅ | stateful wrappers around inputs |
| CSS/Inline Styles | ✅ | ✅ | syntax for inline styles/attribute groups[2] |
| NodeRef | 🛠 | ✅ | gain direct access to nodes [1] |
| 1st class global state | 🛠 | ✅ | redux/recoil/mobx on top of context |
| CSS/Inline Styles | ✅ | ✅ | syntax for inline styles/attribute groups |
| Custom elements | ✅ | ✅ | Define new element primitives |
| Compile-time correct | ✅ | ✅ | Throw errors on invalid template layouts |
| 1st class global state | ✅ | ❓ | redux/recoil/mobx on top of context |
| 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 |
| Runs natively | ✅ | ❓ | runs as a portable binary w/o a runtime (Node) |
| NodeRef | 🛠 | ✅ | gain direct access to nodes [1] |
- [1] Currently blocked until we figure out a cross-platform way of exposing an imperative Node API.
- [2] Would like to solve this in a more general way. Something like attribute groups that's not styling-specific.
### Phase 2: Advanced Toolkits

View file

@ -2,8 +2,7 @@
//!
//! The example from the README.md
use dioxus::{events::on::MouseEvent, prelude::*};
use dioxus_html_namespace::{button, div, h1};
use dioxus::prelude::*;
fn main() {
dioxus::web::launch(Example)
@ -12,11 +11,27 @@ fn main() {
fn Example(cx: Context<()>) -> VNode {
let name = use_state(&cx, || "..?");
let handler = move |e: MouseEvent| e.cl;
cx.render(rsx! {
h1 { "Hello, {name}" }
button { "?", onclick: move |event| name.set("world!")}
button { "?", onclick: move |_| name.set("world!")}
button { "?", onclick: move |_| name.set("Dioxus 🎉")}
})
}
static Example2: FC<()> = |cx| {
let (g, set_g) = use_state_classic(&cx, || 0);
let v = (0..10).map(|f| {
dioxus::prelude::LazyNodes::new(move |__cx: &NodeFactory| {
__cx.element(dioxus_elements::li)
.listeners([dioxus::events::on::onclick(__cx, move |_| set_g(10))])
.finish()
})
});
cx.render(dioxus::prelude::LazyNodes::new(
move |__cx: &NodeFactory| {
__cx.element(dioxus_elements::div)
.children([__cx.fragment_from_iter(v)])
.finish()
},
))
};

View file

@ -20,23 +20,6 @@ pub fn html(s: TokenStream) -> TokenStream {
}
}
/// The html! macro makes it easy for developers to write jsx-style markup in their components.
/// ```
/// rsx! {
/// div {
/// class: "some special class"
/// h1 { "Children too" }
/// }
/// }
/// ```
#[proc_macro]
pub fn rsx(s: TokenStream) -> TokenStream {
match syn::parse::<rsx::RsxRender>(s) {
Err(e) => e.to_compile_error().into(),
Ok(s) => s.to_token_stream().into(),
}
}
/// The html! macro makes it easy for developers to write jsx-style markup in their components.
/// We aim to keep functional parity with html templates.
#[proc_macro]
@ -97,3 +80,165 @@ pub fn derive_typed_builder(input: proc_macro::TokenStream) -> proc_macro::Token
Err(error) => error.to_compile_error().into(),
}
}
/// The html! macro makes it easy for developers to write jsx-style markup in their components.
///
/// ## Complete Reference Guide:
/// ```
/// const Example: FC<()> = |cx| {
/// let formatting = "formatting!";
/// let formatting_tuple = ("a", "b");
/// let lazy_fmt = format_args!("lazily formatted text");
/// cx.render(rsx! {
/// div {
/// // Elements
/// div {}
/// h1 {"Some text"}
/// h1 {"Some text with {formatting}"}
/// h1 {"Formatting basic expressions {formatting_tuple.0} and {formatting_tuple.1}"}
/// h2 {
/// "Multiple"
/// "Text"
/// "Blocks"
/// "Use comments as separators in html"
/// }
/// div {
/// h1 {"multiple"}
/// h2 {"nested"}
/// h3 {"elements"}
/// }
/// div {
/// class: "my special div"
/// h1 {"Headers and attributes!"}
/// }
/// div {
/// // pass simple rust expressions in
/// class: lazy_fmt,
/// id: format_args!("attributes can be passed lazily with std::fmt::Arguments"),
/// div {
/// class: {
/// const WORD: &str = "expressions";
/// format_args!("Arguments can be passed in through curly braces for complex {}", WORD)
/// }
/// }
/// }
///
/// // Expressions can be used in element position too:
/// {rsx!(p { "More templating!" })}
/// {html!(<p>"Even HTML templating!!"</p>)}
///
/// // Iterators
/// {(0..10).map(|i| rsx!(li { "{i}" }))}
/// {{
/// let data = std::collections::HashMap::<&'static str, &'static str>::new();
/// // Iterators *should* have keys when you can provide them.
/// // Keys make your app run faster. Make sure your keys are stable, unique, and predictable.
/// // Using an "ID" associated with your data is a good idea.
/// data.into_iter().map(|(k, v)| rsx!(li { key: "{k}" "{v}" }))
/// }}
///
/// // Matching
/// // Matching will throw a Rust error about "no two closures are the same type"
/// // To fix this, call "render" method or use the "in" syntax to produce VNodes.
/// // There's nothing we can do about it, sorry :/ (unless you want *really* unhygenic macros)
/// {match true {
/// true => rsx!(in cx, h1 {"Top text"}),
/// false => cx.render(rsx!( h1 {"Bottom text"}))
/// }}
///
/// // Conditional rendering
/// // Dioxus conditional rendering is based around None/Some. We have no special syntax for conditionals.
/// // You can convert a bool condition to rsx! with .then and .or
/// {true.then(|| rsx!(div {}))}
///
/// // True conditions need to be rendered (same reasons as matching)
/// {if true {
/// rsx!(in cx, h1 {"Top text"})
/// } else {
/// rsx!(in cx, h1 {"Bottom text"})
/// }}
///
/// // returning "None" is a bit noisy... but rare in practice
/// {None as Option<()>}
///
/// // Use the Dioxus type-alias for less noise
/// {NONE_ELEMENT}
///
/// // can also just use empty fragments
/// Fragment {}
///
/// // Fragments let you insert groups of nodes without a parent.
/// // This lets you make components that insert elements as siblings without a container.
/// div {"A"}
/// Fragment {
/// div {"B"}
/// div {"C"}
/// Fragment {
/// "D"
/// Fragment {
/// "heavily nested fragments is an antipattern"
/// "they cause Dioxus to do unnecessary work"
/// "don't use them carelessly if you can help it"
/// }
/// }
/// }
///
/// // Components
/// // Can accept any paths
/// // Notice how you still get syntax highlighting and IDE support :)
/// Baller {}
/// baller::Baller { }
/// crate::baller::Baller {}
///
/// // Can take properties
/// Taller { a: "asd" }
///
/// // Can take optional properties
/// Taller { a: "asd" }
///
/// // Can pass in props directly as an expression
/// {{
/// let props = TallerProps {a: "hello"};
/// rsx!(Taller { ..props })
/// }}
///
/// // Spreading can also be overridden manually
/// Taller {
/// ..TallerProps { a: "ballin!" }
/// a: "not ballin!"
/// }
///
/// // Can take children too!
/// Taller { a: "asd", div {"hello world!"} }
/// }
/// })
/// };
///
/// mod baller {
/// use super::*;
/// pub struct BallerProps {}
///
/// /// This component totally balls
/// pub fn Baller(cx: Context<()>) -> VNode {
/// todo!()
/// }
/// }
///
/// #[derive(Debug, PartialEq, Props)]
/// pub struct TallerProps {
/// a: &'static str,
/// }
///
/// /// This component is taller than most :)
/// pub fn Taller(cx: Context<TallerProps>) -> VNode {
/// let b = true;
/// todo!()
/// }
/// ```
#[proc_macro]
pub fn rsx(s: TokenStream) -> TokenStream {
match syn::parse::<rsx::RsxRender>(s) {
Err(e) => e.to_compile_error().into(),
Ok(s) => s.to_token_stream().into(),
}
}

View file

@ -25,7 +25,7 @@ impl ToTokens for Element {
// let name = &self.name.to_string();
tokens.append_all(quote! {
__cx.element(#name)
__cx.element(dioxus_elements::#name)
});
// Add attributes

View file

@ -80,7 +80,7 @@ impl ToTokens for RsxRender {
quote! {#inner}
} else {
let childs = &self.roots;
quote! { __cx.fragment_from_iter(&[ #(#childs),* ]) }
quote! { __cx.fragment_from_iter([ #(#childs),* ]) }
};
match &self.custom_context {

View file

@ -1,27 +1,29 @@
fn main() {}
use dioxus_core as dioxus;
use dioxus_core::prelude::*;
mod dioxus_elements {
use super::*;
pub struct div;
impl DioxusElement for div {
const TAG_NAME: &'static str = "str";
const NAME_SPACE: Option<&'static str> = None;
}
}
static Example: FC<()> = |cx| {
cx.render(dioxus_core::prelude::LazyNodes::new(move |cx| {
let list = (0..10).map(|f| {
//
LazyNodes::new(move |f: NodeFactory| todo!())
});
cx.render(LazyNodes::new(move |cx| {
let bump = cx.bump();
dioxus_core::builder::ElementBuilder::new(cx, "h1")
.children([dioxus_core::builder::text3(bump, format_args!("hello"))])
.finish()
}))
};
struct Props {
text: String,
}
static Example2: FC<Props> = |cx| {
cx.render(dioxus_core::prelude::LazyNodes::new(move |__cx| {
let bump = __cx.bump();
dioxus_core::builder::ElementBuilder::new(__cx, "h1")
.children([dioxus_core::builder::text3(
bump,
format_args!("{}", cx.props.text),
)])
dioxus_core::builder::ElementBuilder::new(&cx, "h1")
.children([
cx.text(format_args!("hello")),
cx.text(format_args!("hello")),
cx.fragment_from_iter(list),
])
.finish()
}))
};

View file

@ -49,10 +49,23 @@ pub fn fc_to_builder<T: Properties>(_: FC<T>) -> T::Builder {
///
/// Fragments are incredibly useful when necessary, but *do* add cost in the diffing phase.
/// Try to avoid nesting fragments if you can. Infinitely nested Fragments *will* cause diffing to crash.
#[allow(non_upper_case_globals)]
pub const Fragment: FC<()> = |cx| {
use crate::prelude::*;
cx.render(LazyNodes::new(move |c| {
crate::nodebuilder::vfragment(c, None, cx.children())
use crate::prelude::*;
pub fn Fragment<'a>(cx: Context<'a, ()>) -> VNode<'a> {
let childs: &'a [VNode<'a>] = cx.children();
cx.render(LazyNodes::new({
move |f| {
//
f.fragment_from_iter(childs)
}
}))
};
}
// #[allow(non_upper_case_globals)]
// pub const Fragment: FC<()> = |cx| {
// use crate::prelude::*;
// let childs = cx.children();
// cx.render(LazyNodes::new(move |c| {
// c.fragment_from_iter(childs)
// // c.text(format_args!(""))
// // crate::nodebuilder::vfragment(c, None, cx.children())
// }))
// };

View file

@ -49,18 +49,18 @@ impl DebugRenderer {
// If you have a list or "nth" child, you do need to list those children, but you don't need to
// fill in their children/attrs/etc
// Does not handle children or lifecycles and will always fail the test if they show up in the rhs
pub fn compare<'a, F>(&self, other: LazyNodes<'a, F>) -> Result<()>
pub fn compare<F>(&self, other: LazyNodes<F>) -> Result<()>
where
F: for<'b> FnOnce(&'b NodeFactory<'a>) -> VNode<'a> + 'a,
F: for<'b, 'c> FnOnce(&'b NodeFactory<'c>) -> VNode<'c>,
{
Ok(())
}
// Do a full compare - everything must match
// Ignores listeners and children components
pub fn compare_full<'a, F>(&self, other: LazyNodes<'a, F>) -> Result<()>
pub fn compare_full<F>(&self, other: LazyNodes<F>) -> Result<()>
where
F: for<'b> FnOnce(&'b NodeFactory<'a>) -> VNode<'a> + 'a,
F: for<'b, 'c> FnOnce(&'b NodeFactory<'c>) -> VNode<'c>,
{
Ok(())
}
@ -69,9 +69,9 @@ impl DebugRenderer {
Ok(())
}
pub fn render_nodes<'a, F>(&self, other: LazyNodes<'a, F>) -> Result<()>
pub fn render_nodes<F>(&self, other: LazyNodes<F>) -> Result<()>
where
F: for<'b> FnOnce(&'b NodeFactory<'a>) -> VNode<'a> + 'a,
F: for<'b, 'c> FnOnce(&'b NodeFactory<'c>) -> VNode<'c>,
{
Ok(())
}

View file

@ -124,14 +124,12 @@ pub mod on {
macro_rules! event_directory {
( $(
$(
#[$attr:meta]
)*
$( #[$attr:meta] )*
$eventdata:ident($wrapper:ident): [
$(
#[$method_attr:meta]
$( #[$method_attr:meta] )*
$name:ident
)*
$( $name:ident )*
];
)* ) => {
$(
@ -149,8 +147,8 @@ pub mod on {
}
}
$(#[$method_attr])*
$(
$(#[$method_attr])*
pub fn $name<'a>(
c: &'_ NodeFactory<'a>,
callback: impl Fn($wrapper) + 'a,
@ -181,35 +179,51 @@ pub mod on {
//
//
event_directory! {
ClipboardEventInner(ClipboardEvent): [
/// Called when "copy"
oncopy
/// oncut
oncut
/// onpaste
onpaste
];
CompositionEventInner(CompositionEvent): [
/// oncompositionend
oncompositionend
/// oncompositionstart
oncompositionstart
/// oncompositionupdate
oncompositionupdate
];
KeyboardEventInner(KeyboardEvent): [
/// onkeydown
onkeydown
/// onkeypress
onkeypress
/// onkeyup
onkeyup
];
FocusEventInner(FocusEvent): [
/// onfocus
onfocus
/// onblur
onblur
];
FormEventInner(FormEvent): [
/// onchange
onchange
/// oninput
oninput
/// oninvalid
oninvalid
/// onreset
onreset
/// onsubmit
onsubmit
];
@ -237,63 +251,202 @@ pub mod on {
/// ```
///
/// ## Event Handlers
/// - click
/// - contextmenu
/// - doubleclick
/// - drag
/// - dragend
/// - dragenter
/// - dragexit
/// - dragleave
/// - dragover
/// - dragstart
/// - drop
/// - mousedown
/// - mouseenter
/// - mouseleave
/// - mousemove
/// - mouseout
/// - mouseover
/// - mouseup
/// - [`onclick`]
/// - [`oncontextmenu`]
/// - [`ondoubleclick`]
/// - [`ondrag`]
/// - [`ondragend`]
/// - [`ondragenter`]
/// - [`ondragexit`]
/// - [`ondragleave`]
/// - [`ondragover`]
/// - [`ondragstart`]
/// - [`ondrop`]
/// - [`onmousedown`]
/// - [`onmouseenter`]
/// - [`onmouseleave`]
/// - [`onmousemove`]
/// - [`onmouseout`]
/// - [`onmouseover`]
/// - [`onmouseup`]
MouseEventInner(MouseEvent): [
/// Onclick!
/// Execute a callback when a button is clicked.
///
/// ## Description
///
/// An element receives a click event when a pointing device button (such as a mouse's primary mouse button)
/// is both pressed and released while the pointer is located inside the element.
///
/// - Bubbles: Yes
/// - Cancelable: Yes
/// - Interface: [`MouseEvent`]
///
/// If the button is pressed on one element and the pointer is moved outside the element before the button
/// is released, the event is fired on the most specific ancestor element that contained both elements.
/// `click` fires after both the `mousedown` and `mouseup` events have fired, in that order.
///
/// ## Example
/// ```
/// rsx!( button { "click me", onclick: move |_| log::info!("Clicked!`") } )
/// ```
///
/// ## Reference
/// - https://www.w3schools.com/tags/ev_onclick.asp
/// - https://developer.mozilla.org/en-US/docs/Web/API/Element/click_event
///
onclick
/// oncontextmenu
oncontextmenu
/// ondoubleclick
ondoubleclick
/// ondrag
ondrag
/// ondragend
ondragend
/// ondragenter
ondragenter
/// ondragexit
ondragexit
/// ondragleave
ondragleave
/// ondragover
ondragover
/// ondragstart
ondragstart
/// ondrop
ondrop
/// onmousedown
onmousedown
/// onmouseenter
onmouseenter
/// onmouseleave
onmouseleave
/// onmousemove
onmousemove
/// onmouseout
onmouseout
/// onmouseover
onmouseover
/// onmouseup
onmouseup
];
PointerEventInner(PointerEvent): [
pointerdown pointermove pointerup pointercancel gotpointercapture
lostpointercapture pointerenter pointerleave pointerover pointerout
/// pointerdown
onpointerdown
/// pointermove
onpointermove
/// pointerup
onpointerup
/// pointercancel
onpointercancel
/// gotpointercapture
ongotpointercapture
/// lostpointercapture
onlostpointercapture
/// pointerenter
onpointerenter
/// pointerleave
onpointerleave
/// pointerover
onpointerover
/// pointerout
onpointerout
];
SelectionEventInner(SelectionEvent): [select];
TouchEventInner(TouchEvent): [touchcancel touchend touchmove touchstart];
UIEventInner(UIEvent): [scroll];
WheelEventInner(WheelEvent): [wheel];
SelectionEventInner(SelectionEvent): [
/// onselect
onselect
];
TouchEventInner(TouchEvent): [
/// ontouchcancel
ontouchcancel
/// ontouchend
ontouchend
/// ontouchmove
ontouchmove
/// ontouchstart
ontouchstart
];
UIEventInner(UIEvent): [
///
scroll
];
WheelEventInner(WheelEvent): [
///
wheel
];
MediaEventInner(MediaEvent): [
abort canplay canplaythrough durationchange emptied encrypted
ended error loadeddata loadedmetadata loadstart pause play
playing progress ratechange seeked seeking stalled suspend
timeupdate volumechange waiting
///abort
onabort
///canplay
oncanplay
///canplaythrough
oncanplaythrough
///durationchange
ondurationchange
///emptied
onemptied
///encrypted
onencrypted
///ended
onended
///error
onerror
///loadeddata
onloadeddata
///loadedmetadata
onloadedmetadata
///loadstart
onloadstart
///pause
onpause
///play
onplay
///playing
onplaying
///progress
onprogress
///ratechange
onratechange
///seeked
onseeked
///seeking
onseeking
///stalled
onstalled
///suspend
onsuspend
///timeupdate
ontimeupdate
///volumechange
onvolumechange
///waiting
onwaiting
];
AnimationEventInner(AnimationEvent): [
/// onanimationstart
onanimationstart
/// onanimationend
onanimationend
/// onanimationiteration
onanimationiteration
];
TransitionEventInner(TransitionEvent): [
///
ontransitionend
];
ToggleEventInner(ToggleEvent): [
///
ontoggle
];
AnimationEventInner(AnimationEvent): [animationstart animationend animationiteration];
TransitionEventInner(TransitionEvent): [transitionend];
ToggleEventInner(ToggleEvent): [toggle];
}
pub trait GenericEventInner {
@ -404,7 +557,6 @@ pub mod on {
fn alt_key(&self) -> bool;
fn button(&self) -> i16;
fn buttons(&self) -> u16;
/// Get the X coordinate of the mouse relative to the window
fn client_x(&self) -> i32;
fn client_y(&self) -> i32;
@ -709,26 +861,3 @@ pub mod on {
}
}
}
// mod tests {
// use std::rc::Rc;
// use crate as dioxus;
// use crate::events::on::MouseEvent;
// use crate::prelude::*;
// fn autocomplete() {
// // let v = move |evt| {
// // let r = evt.alt_key();
// // };
// let g = rsx! {
// button {
// onclick: move |evt| {
// let r = evt.alt_key();
// }
// }
// };
// }
// }

View file

@ -16,7 +16,7 @@ pub mod component;
pub mod styles;
pub mod util; // Logic for extending FC
pub mod debug_renderer;
// pub mod debug_renderer;
pub mod diff;
pub mod error; // Error type we expose to the renderers
@ -62,7 +62,6 @@ pub mod prelude {
pub use crate::nodebuilder::LazyNodes;
pub use crate::nodebuilder::ChildrenList;
pub use crate::nodebuilder::{DioxusElement, NodeFactory};
// pub use nodes::iterables::IterableNodes;
/// This type alias is an internal way of abstracting over the static functions that represent components.

View file

@ -6,6 +6,7 @@ use std::{
cell::{Cell, RefCell},
fmt::Arguments,
intrinsics::transmute,
marker::PhantomData,
u128,
};
@ -511,8 +512,9 @@ where
pub fn iter_child(mut self, nodes: impl IntoIterator<Item = impl IntoVNode<'a>>) -> Self {
let len_before = self.children.len();
for item in nodes {
let child = item.into_vnode(&self.cx);
self.children.push(child);
todo!()
// let child = item.into_vnode(&self.cx);
// self.children.push(child);
}
if cfg!(debug_assertions) {
if self.children.len() > len_before + 1 {
@ -541,52 +543,53 @@ impl<'a> IntoIterator for VNode<'a> {
}
}
impl<'a> IntoVNode<'a> for VNode<'a> {
fn into_vnode(self, cx: &NodeFactory<'a>) -> VNode<'a> {
fn into_vnode(self, cx: NodeFactory<'a>) -> VNode<'a> {
self
}
}
impl<'a> IntoVNode<'a> for &VNode<'a> {
fn into_vnode(self, cx: &NodeFactory<'a>) -> VNode<'a> {
fn into_vnode(self, cx: NodeFactory<'a>) -> VNode<'a>
// where
// 'a: 'c,
{
todo!()
// cloning is cheap since vnodes are just references into bump arenas
self.clone()
// self.clone()
}
}
pub trait IntoVNode<'a> {
fn into_vnode(self, cx: &NodeFactory<'a>) -> VNode<'a>;
fn into_vnode(self, cx: NodeFactory<'a>) -> VNode<'a>;
}
pub trait VNodeBuilder<'a, G>: IntoIterator<Item = G>
where
G: IntoVNode<'a>,
{
}
// pub trait VNodeBuilder<'a, G>: IntoIterator<Item = G>
// where
// G: IntoVNode<'a>,
// {
// }
impl<'a, F> VNodeBuilder<'a, LazyNodes<'a, F>> for LazyNodes<'a, F> where
F: for<'b> FnOnce(&'b NodeFactory<'a>) -> VNode<'a> + 'a
{
}
// impl<'a, F> VNodeBuilder<'a, LazyNodes<F>> for LazyNodes<F> where F: FnOnce(NodeFactory) -> VNode {}
// Wrap the the node-builder closure in a concrete type.
// ---
// This is a bit of a hack to implement the IntoVNode trait for closure types.
pub struct LazyNodes<'a, G>
where
G: for<'b> FnOnce(&'b NodeFactory<'a>) -> VNode<'a> + 'a,
G: FnOnce(NodeFactory<'a>) -> VNode<'a>,
{
inner: G,
_p: std::marker::PhantomData<&'a ()>,
_p: PhantomData<&'a ()>,
}
impl<'a, G> LazyNodes<'a, G>
where
G: for<'b> FnOnce(&'b NodeFactory<'a>) -> VNode<'a> + 'a,
G: FnOnce(NodeFactory<'a>) -> VNode<'a>,
{
pub fn new(f: G) -> Self {
Self {
inner: f,
_p: std::default::Default::default(),
_p: PhantomData {},
}
}
}
@ -598,9 +601,9 @@ where
// rsx! { {nodes } }
impl<'a, G> IntoVNode<'a> for LazyNodes<'a, G>
where
G: for<'b> FnOnce(&'b NodeFactory<'a>) -> VNode<'a> + 'a,
G: FnOnce(NodeFactory<'a>) -> VNode<'a>,
{
fn into_vnode(self, cx: &NodeFactory<'a>) -> VNode<'a> {
fn into_vnode(self, cx: NodeFactory<'a>) -> VNode<'a> {
(self.inner)(cx)
}
}
@ -608,7 +611,7 @@ where
// Required because anything that enters brackets in the rsx! macro needs to implement IntoIterator
impl<'a, G> IntoIterator for LazyNodes<'a, G>
where
G: for<'b> FnOnce(&'b NodeFactory<'a>) -> VNode<'a> + 'a,
G: FnOnce(NodeFactory<'a>) -> VNode<'a>,
{
type Item = Self;
type IntoIter = std::iter::Once<Self::Item>;
@ -617,23 +620,23 @@ where
}
}
impl<'a> IntoVNode<'a> for () {
fn into_vnode(self, cx: &NodeFactory<'a>) -> VNode<'a> {
todo!();
VNode::Suspended {
real: Cell::new(RealDomNode::empty()),
}
}
}
// impl IntoVNode<'_> for () {
// fn into_vnode<'a>(self, cx: NodeFactory<'a>) -> VNode<'a> {
// todo!();
// VNode::Suspended {
// real: Cell::new(RealDomNode::empty()),
// }
// }
// }
impl<'a> IntoVNode<'a> for Option<()> {
fn into_vnode(self, cx: &NodeFactory<'a>) -> VNode<'a> {
todo!();
VNode::Suspended {
real: Cell::new(RealDomNode::empty()),
}
}
}
// impl IntoVNode<'_> for Option<()> {
// fn into_vnode<'a>(self, cx: NodeFactory<'a>) -> VNode<'a> {
// todo!();
// VNode::Suspended {
// real: Cell::new(RealDomNode::empty()),
// }
// }
// }
/// Construct a text VNode.
///
@ -702,7 +705,7 @@ pub fn attr<'a>(name: &'static str, value: &'a str) -> Attribute<'a> {
}
pub fn virtual_child<'a, T: Properties + 'a>(
cx: &NodeFactory<'a>,
cx: NodeFactory<'a>,
f: FC<T>,
props: T,
key: Option<&'a str>, // key: NodeKey<'a>,
@ -713,54 +716,28 @@ pub fn virtual_child<'a, T: Properties + 'a>(
// todo!()
VNode::Component(
cx.bump()
.alloc(crate::nodes::VComponent::new(cx, f, props, key, children)),
.alloc(crate::nodes::VComponent::new(&cx, f, props, key, children)),
// cx.bump()
// .alloc(crate::nodes::VComponent::new(f, props, key)),
)
}
pub fn vfragment<'a>(
cx: &NodeFactory<'a>,
cx: NodeFactory<'a>,
key: Option<&'a str>, // key: NodeKey<'a>,
children: &'a [VNode<'a>],
) -> VNode<'a> {
VNode::Fragment(cx.bump().alloc(VFragment::new(key, children)))
}
pub struct ChildrenList<'a, 'b> {
cx: &'b NodeFactory<'a>,
children: bumpalo::collections::Vec<'a, VNode<'a>>,
}
impl<'a, 'b> ChildrenList<'a, 'b> {
pub fn new(cx: &'b NodeFactory<'a>) -> Self {
Self {
cx,
children: bumpalo::collections::Vec::new_in(cx.bump()),
}
}
pub fn add_child(mut self, nodes: impl IntoIterator<Item = impl IntoVNode<'a>>) -> Self {
for item in nodes {
let child = item.into_vnode(&self.cx);
self.children.push(child);
}
self
}
pub fn finish(self) -> &'a [VNode<'a>] {
self.children.into_bump_slice()
}
}
/// This struct provides an ergonomic API to quickly build VNodes.
///
/// NodeFactory is used to build VNodes in the component's memory space.
/// This struct adds metadata to the final VNode about listeners, attributes, and children
#[derive(Clone)]
#[derive(Copy, Clone)]
pub struct NodeFactory<'a> {
pub scope_ref: &'a Scope,
pub listener_id: Cell<usize>,
pub listener_id: &'a Cell<usize>,
}
impl<'a> NodeFactory<'a> {
@ -791,7 +768,7 @@ impl<'a> NodeFactory<'a> {
pub fn attr(
&self,
name: &'static str,
val: Arguments<'a>,
val: Arguments,
namespace: Option<&'static str>,
) -> Attribute<'a> {
let value = raw_text(self.bump(), val);
@ -802,18 +779,6 @@ impl<'a> NodeFactory<'a> {
}
}
pub fn child_list(&self) -> ChildrenList {
ChildrenList::new(&self)
}
pub fn fragment_builder<'b>(
&'b self,
key: Option<&'a str>,
builder: impl FnOnce(ChildrenList<'a, 'b>) -> &'a [VNode<'a>],
) -> VNode<'a> {
self.fragment(builder(ChildrenList::new(&self)), key)
}
pub fn fragment(&self, children: &'a [VNode<'a>], key: Option<&'a str>) -> VNode<'a> {
VNode::Fragment(self.bump().alloc(VFragment {
children,
@ -822,12 +787,12 @@ impl<'a> NodeFactory<'a> {
}
pub fn fragment_from_iter(
&self,
self,
node_iter: impl IntoIterator<Item = impl IntoVNode<'a>>,
) -> VNode<'a> {
let mut nodes = bumpalo::collections::Vec::new_in(self.bump());
for node in node_iter.into_iter() {
nodes.push(node.into_vnode(&self));
nodes.push(node.into_vnode(self));
}
VNode::Fragment(
self.bump()

View file

@ -393,6 +393,8 @@ pub struct Scope {
// could also use ourborous
hooks: RefCell<Vec<Hook>>,
pub(crate) listener_idx: Cell<usize>,
// Unsafety:
// - is self-refenrential and therefore needs to point into the bump
// Stores references into the listeners attached to the vnodes
@ -463,6 +465,7 @@ impl Scope {
height,
event_channel,
arena_link,
listener_idx: Default::default(),
frames: ActiveFrame::new(),
hooks: Default::default(),
shared_contexts: Default::default(),
@ -506,13 +509,9 @@ impl Scope {
// Remove all the outdated listeners
self.listeners.borrow_mut().clear();
// self.listeners
// .try_borrow_mut()
// .ok()
// .ok_or(Error::FatalInternal("Borrowing listener failed"))?
// .drain(..);
self.hookidx.set(0);
self.listener_idx.set(0);
let caller = self
.caller
@ -522,12 +521,8 @@ impl Scope {
// Cast the caller ptr from static to one with our own reference
let c2: &OpaqueComponent = caller.as_ref();
let c3: &OpaqueComponent = unsafe { std::mem::transmute(c2) };
// let c2: &OpaqueComponent<'static> = caller.as_ref();
// let c3: &OpaqueComponent<'sel> = unsafe { std::mem::transmute(c2) };
let unsafe_head = unsafe { self.own_vnodes(c3) };
self.frames.cur_frame_mut().head_node = unsafe_head;
self.frames.cur_frame_mut().head_node = unsafe { self.own_vnodes(c3) };
Ok(())
}
@ -685,13 +680,15 @@ pub trait Scoped<'src>: Sized {
/// cx.render(lazy_tree)
/// }
///```
fn render<'a, F: for<'b> FnOnce(&'b NodeFactory<'src>) -> VNode<'src> + 'src + 'a>(
fn render<F: FnOnce(NodeFactory<'src>) -> VNode<'src>>(
self,
lazy_nodes: LazyNodes<'src, F>,
) -> VNode<'src> {
lazy_nodes.into_vnode(&NodeFactory {
scope_ref: self.get_scope(),
listener_id: 0.into(),
let scope_ref = self.get_scope();
let listener_id = &scope_ref.listener_idx;
lazy_nodes.into_vnode(NodeFactory {
scope_ref,
listener_id,
})
}
@ -890,14 +887,14 @@ Any function prefixed with "use" should not be called conditionally.
#[derive(Clone)]
pub struct SuspendedContext {}
impl SuspendedContext {
pub fn render<'a, 'src, F: for<'b> FnOnce(&'b NodeFactory<'src>) -> VNode<'src> + 'src + 'a>(
self,
lazy_nodes: LazyNodes<'src, F>,
) -> VNode<'src> {
todo!()
}
}
// impl SuspendedContext {
// pub fn render<'a, F: for<'b, 'src> FnOnce(&'b NodeFactory<'src>) -> VNode<'src>>(
// &self,
// lazy_nodes: LazyNodes<F>,
// ) -> VNode {
// todo!()
// }
// }
// ==================================================================================
// Supporting structs for the above abstractions

View file

@ -26,7 +26,18 @@ macro_rules! builder_constructors {
( $(
$(#[$attr:meta])*
$name:ident <> $namespace:tt;
)* ) => {};
)* ) => {
$(
#[allow(non_camel_case_types)]
$(#[$attr])*
pub struct $name;
impl DioxusElement for $name {
const TAG_NAME: &'static str = stringify!($name);
const NAME_SPACE: Option<&'static str> = Some(stringify!($namespace));
}
)*
};
}
// Organized in the same order as
@ -554,6 +565,7 @@ builder_constructors! {
/// [`<slot>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/slot)
/// element.
slot;
/// Build a
/// [`<template>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/template)
/// element.

View file

@ -9,6 +9,7 @@ license = "MIT/Apache-2.0"
[dependencies]
dioxus-core = { path="../core", version="0.1.2" }
dioxus-html-namespace = { path="../html-namespace" }
js-sys = "0.3"
wasm-bindgen = { version="0.2.71", features=["enable-interning"] }
lazy_static = "1.4.0"

View file

@ -3,6 +3,7 @@
use dioxus::events::on::MouseEvent;
use dioxus_core as dioxus;
use dioxus_core::prelude::*;
use dioxus_html_namespace as dioxus_elements;
use dioxus_web::*;
fn main() {

View file

@ -9,6 +9,7 @@
use dioxus::events::on::*;
use dioxus::prelude::*;
use dioxus_core as dioxus;
use dioxus_html_namespace as dioxus_elements;
use dioxus_web::WebsysRenderer;
const STYLE: &str = include_str!("../../../examples/assets/calculator.css");
@ -159,7 +160,7 @@ struct CalculatorKeyProps<'a> {
onclick: &'a dyn Fn(MouseEvent),
}
fn CalculatorKey<'a>(cx: Context<'a, CalculatorKeyProps>) -> VNode<'a> {
fn CalculatorKey<'a, 'r>(cx: Context<'a, CalculatorKeyProps<'r>>) -> VNode<'a> {
cx.render(rsx! {
button {
class: "calculator-key {cx.name}"
@ -181,7 +182,7 @@ fn CalculatorDisplay<'a>(cx: Context<'a, CalculatorDisplayProps>) -> VNode<'a> {
// TODO: make it autoscaling with css
cx.render(rsx! {
div { class: "calculator-display"
div { class: "auto-scaling-text", "{display_value}" }
div { class: "auto-scaling-text", "{formatted}" }
}
})
}

View file

@ -1,6 +1,7 @@
//! Basic example that renders a simple VNode to the browser.
use dioxus_core::prelude::*;
use dioxus_html_namespace as dioxus_elements;
use dioxus_web::*;
fn main() {

View file

@ -14,6 +14,7 @@
use dioxus_core::prelude::*;
use dioxus_core as dioxus;
use dioxus_web::WebsysRenderer;
use dioxus_html_namespace as dioxus_elements;
fn main() {
wasm_logger::init(wasm_logger::Config::new(log::Level::Trace));

View file

@ -1,13 +1,14 @@
use std::rc::Rc;
use dioxus::prelude::*;
use dioxus_core as dioxus;
use dioxus_web::{dioxus::prelude::*, WebsysRenderer};
use dioxus_html_namespace as dioxus_elements;
fn main() {
wasm_logger::init(wasm_logger::Config::new(log::Level::Debug));
console_error_panic_hook::set_once();
wasm_bindgen_futures::spawn_local(WebsysRenderer::start(CustomA))
wasm_bindgen_futures::spawn_local(dioxus_web::WebsysRenderer::start(CustomA))
}
fn CustomA(cx: Context<()>) -> VNode {

View file

@ -1,4 +1,6 @@
use dioxus_core as dioxus;
use dioxus_html_namespace as dioxus_elements;
use dioxus_html_namespace::*;
use dioxus_web::{dioxus::prelude::*, WebsysRenderer};
fn main() {
@ -9,7 +11,7 @@ fn main() {
fn App(cx: Context<()>) -> VNode {
cx.render(rsx! {
main { class: "dark:bg-gray-800 bg-white relative h-screen"
div { class: "dark:bg-gray-800 bg-white relative h-screen"
NavBar {}
{(0..10).map(|f| rsx!(Landing { key: "{f}" }))}
}

View file

@ -1,5 +1,6 @@
use dioxus::{events::on::MouseEvent, prelude::*};
use dioxus_core as dioxus;
use dioxus_html_namespace as dioxus_elements;
use dioxus_web::WebsysRenderer;
fn main() {

View file

@ -1,6 +1,7 @@
//! Basic example that renders a simple VNode to the browser.
use dioxus_core::prelude::*;
use dioxus_html_namespace as dioxus_elements;
use dioxus_web::*;
fn main() {

View file

@ -12,6 +12,9 @@ use dioxus::events::on::MouseEvent;
use dioxus_core as dioxus;
use dioxus_core::prelude::*;
use dioxus_web::WebsysRenderer;
use dioxus_html_namespace as dioxus_elements;
fn main() {
wasm_logger::init(wasm_logger::Config::new(log::Level::Debug));
@ -21,7 +24,8 @@ fn main() {
}
// We use a special immutable hashmap to make hashmap operations efficient
type RowList = im_rc::HashMap<usize, Rc<str>, nohash_hasher::BuildNoHashHasher<usize>>;
type RowList = im_rc::HashMap<usize, Rc<str>, FxBuildHasher>;
// type RowList = im_rc::HashMap<usize, Rc<str>, nohash_hasher::BuildNoHashHasher<usize>>;
static App: FC<()> = |cx| {
let (items, set_items) = use_state_classic(&cx, || RowList::default());
@ -90,15 +94,15 @@ static App: FC<()> = |cx| {
};
#[derive(Props)]
struct ActionButtonProps<F: Fn(Rc<dyn MouseEvent>)> {
struct ActionButtonProps<F: Fn(MouseEvent)> {
name: &'static str,
id: &'static str,
action: F,
}
fn ActionButton<F: Fn(Rc<dyn MouseEvent>)>(cx: Context<ActionButtonProps<F>>) -> VNode {
fn ActionButton<F: Fn(MouseEvent)>(cx: Context<ActionButtonProps<F>>) -> VNode {
cx.render(rsx! {
div { class: "col-sm-6 smallpad"
button {class:"btn btn-primary btn-block", type: "button", id: "{cx.id}", onclick: {&cx.action},
button {class:"btn btn-primary btn-block", r#type: "button", id: "{cx.id}", onclick: {&cx.action},
"{cx.name}"
}
}
@ -128,6 +132,7 @@ fn Row<'a>(cx: Context<'a, RowProps>) -> VNode {
})
}
use fxhash::{FxBuildHasher, FxHasher32};
use rand::prelude::*;
fn create_new_row_label(rng: &mut SmallRng) -> Rc<str> {
let mut label = String::new();

View file

@ -1,5 +1,6 @@
use dioxus_core as dioxus;
use dioxus_core::prelude::*;
use dioxus_html_namespace as dioxus_elements;
use dioxus_web::WebsysRenderer;
fn main() {

View file

@ -1,4 +1,5 @@
use dioxus_core as dioxus;
use dioxus_html_namespace as dioxus_elements;
use dioxus_web::prelude::*;
fn main() {

View file

@ -1,6 +1,8 @@
use dioxus_core as dioxus;
use dioxus_core::{events::on::MouseEvent, prelude::*};
use dioxus_web::WebsysRenderer;
use dioxus_html_namespace as dioxus_elements;
fn main() {
// Setup logging

View file

@ -1,6 +1,8 @@
use dioxus_core as dioxus;
use dioxus_core::prelude::*;
use dioxus_web::WebsysRenderer;
use dioxus_html_namespace as dioxus_elements;
fn main() {
// wasm_logger::init(wasm_logger::Config::new(log::Level::Debug));
@ -24,7 +26,7 @@ static App: FC<()> = |cx| {
placeholder: "Username"
class: "shadow appearance-none rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
id: "username"
type: "text"
r#type: "text"
value: "{val}"
oninput: move |evet| set_val(evet.value())
}
@ -56,7 +58,7 @@ static UserInput: FC<()> = |cx| {
input { class: "shadow appearance-none rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
placeholder: "Username"
id: "username"
type: "text"
r#type: "text"
oninput: move |evet| set_val(evet.value())
}
p { "Val is: {val}" }

View file

@ -1,5 +1,6 @@
use dioxus::prelude::*;
use dioxus_core as dioxus;
use dioxus_html_namespace as dioxus_elements;
use dioxus_web::WebsysRenderer;
fn main() {

View file

@ -3,7 +3,9 @@
use std::rc::Rc;
use dioxus_core::prelude::*;
use dioxus_html_namespace as dioxus_elements;
use dioxus_web::*;
fn main() {
// Setup logging
// wasm_logger::init(wasm_logger::Config::new(log::Level::Debug));

View file

@ -1,9 +1,9 @@
//! Basic example that renders a simple VNode to the browser.
use std::{future::Future, pin::Pin, rc::Rc};
use dioxus_core::prelude::*;
use dioxus_html_namespace as dioxus_elements;
use dioxus_web::*;
fn main() {
// Setup logging and panic handling
wasm_logger::init(wasm_logger::Config::new(log::Level::Debug));

View file

@ -1,5 +1,6 @@
use dioxus_core as dioxus;
use dioxus_core::prelude::*;
use dioxus_html_namespace as dioxus_elements;
use dioxus_web::WebsysRenderer;
use std::collections::BTreeMap;
@ -72,7 +73,7 @@ static App: FC<()> = |cx| {
"{todo.contents}"
input {
class: "toggle"
type: "checkbox"
r#type: "checkbox"
"{todo.checked}"
}
}

View file

@ -1,5 +1,6 @@
use dioxus_core as dioxus;
use dioxus_core::prelude::*;
use dioxus_html_namespace as dioxus_elements;
use dioxus_web::WebsysRenderer;
fn main() {

View file

@ -1,5 +1,6 @@
use dioxus_core as dioxus;
use dioxus_core::prelude::*;
use dioxus_html_namespace as dioxus_elements;
use dioxus_web::WebsysRenderer;
fn main() {

View file

@ -18,6 +18,7 @@
use dioxus::events::on::*;
use dioxus::prelude::*;
use dioxus_core as dioxus;
use dioxus_html_namespace as dioxus_elements;
use dioxus_web::WebsysRenderer;
const STYLE: &str = include_str!("../../../examples/assets/calculator.css");

View file

@ -4,6 +4,8 @@ use std::rc::Rc;
use dioxus::{events::on::MouseEvent, prelude::*};
use dioxus_core as dioxus;
use dioxus_web::WebsysRenderer;
use dioxus_html_namespace as dioxus_elements;
fn main() {
wasm_logger::init(wasm_logger::Config::new(log::Level::Trace));
@ -50,7 +52,7 @@ static Example: FC<ExampleProps> = |cx| {
};
#[derive(Props)]
struct ButtonProps<'src, F: Fn(Rc<dyn MouseEvent>)> {
struct ButtonProps<'src, F: Fn(MouseEvent)> {
name: &'src str,
handler: F,
}

View file

@ -2,6 +2,7 @@ use std::{collections::HashMap, rc::Rc};
use dioxus_core as dioxus;
use dioxus_core::prelude::*;
use dioxus_html_namespace as dioxus_elements;
use dioxus_web::WebsysRenderer;
static APP_STYLE: &'static str = include_str!("./todomvc/style.css");
@ -112,7 +113,7 @@ pub fn TodoEntry(cx: Context<TodoEntryProps>) -> VNode {
"{todo.id}"
input {
class: "toggle"
type: "checkbox"
r#type: "checkbox"
"{todo.checked}"
}
{is_editing.then(|| rsx!{

View file

@ -1,4 +1,5 @@
use dioxus_core as dioxus;
use dioxus_html_namespace as dioxus_elements;
use dioxus_web::{prelude::*, WebsysRenderer};
// mod filtertoggles;

View file

@ -2,6 +2,7 @@ use std::{collections::HashMap, rc::Rc};
use dioxus_core as dioxus;
use dioxus_core::prelude::*;
use dioxus_html_namespace as dioxus_elements;
use dioxus_web::WebsysRenderer;
static APP_STYLE: &'static str = include_str!("./todomvc/style.css");
@ -115,7 +116,7 @@ pub fn TodoEntry(cx: Context<TodoEntryProps>) -> VNode {
"{todo.id}"
input {
class: "toggle"
type: "checkbox"
r#type: "checkbox"
"{todo.checked}"
}
{is_editing.then(|| rsx!{

View file

@ -169,6 +169,7 @@ pub mod prelude {
//! A glob import that includes helper types like FC, rsx!, html!, and required traits
pub use dioxus_core::prelude::*;
pub use dioxus_core_macro::fc;
pub use dioxus_html_namespace as dioxus_elements;
}
// pub mod builder {
// // pub use dioxus_core::builder::*;