wip: pre vnodes instead of vnode
This commit is contained in:
parent
e3d9db0847
commit
fe6938ceb3
|
@ -5,11 +5,11 @@ members = [
|
|||
"packages/core",
|
||||
"packages/web",
|
||||
"packages/dioxus",
|
||||
"packages/recoil",
|
||||
"packages/docsite",
|
||||
"packages/ssr",
|
||||
"packages/webview",
|
||||
]
|
||||
# "packages/recoil",
|
||||
# "packages/docsite",
|
||||
# "packages/webview",
|
||||
|
||||
# "packages/cli",
|
||||
# "packages/webview",
|
||||
|
|
10
README.md
10
README.md
|
@ -8,15 +8,13 @@
|
|||
Dioxus is a portable, performant, and ergonomic framework for building cross-platform user experiences in Rust.
|
||||
|
||||
```rust
|
||||
fn Example(ctx: Context<()>) -> VNode {
|
||||
fn Example(ctx: Context<()>) -> VNodes {
|
||||
let (selection, set_selection) = use_state(&ctx, move || "..?");
|
||||
|
||||
ctx.render(rsx! {
|
||||
div {
|
||||
h1 { "Hello, {selection}" }
|
||||
button { "?", onclick: move |_| set_selection("world!")}
|
||||
button { "?", onclick: move |_| set_selection("Dioxus 🎉")}
|
||||
}
|
||||
h1 { "Hello, {selection}" }
|
||||
button { "?", onclick: move |_| set_selection("world!")}
|
||||
button { "?", onclick: move |_| set_selection("Dioxus 🎉")}
|
||||
})
|
||||
};
|
||||
```
|
||||
|
|
|
@ -2,41 +2,41 @@
|
|||
|
||||
Sorted by priority
|
||||
|
||||
| Feature | Dioxus | React | Notes |
|
||||
| ---------------------- | ------ | ----- | ------------------------------------ |
|
||||
| ----- Phase 1 ----- | ----- | ----- | ----- |
|
||||
| Conditional Rendering | ✅ | ✅ | if/then to hide/show component |
|
||||
| Map, Iterator | ✅ | ✅ | map/filter/reduce rsx! |
|
||||
| Keyed Components | ✅ | ✅ | advanced diffing with keys |
|
||||
| Web | ✅ | ✅ | renderer for web browser |
|
||||
| Desktop (webview) | ✅ | ✅ | renderer for desktop |
|
||||
| Context | ✅ | ✅ | share state through the tree |
|
||||
| Hook | ✅ | ✅ | memory cells in components |
|
||||
| SSR | ✅ | ✅ | render directly to string |
|
||||
| Runs natively | ✅ | 👀 | runs as a sharable binary |
|
||||
| Null components | 👀 | ✅ | allow returning None |
|
||||
| Fragments | 👀 | ✅ | no requirement on main root |
|
||||
| Component Children | 👀 | ✅ | ctx.children() as a list of nodes |
|
||||
| NodeRef | 👀 | ✅ | gain direct access to nodes |
|
||||
| Controlled Inputs | 👀 | ✅ | stateful wrappers around inputs |
|
||||
| No-div components | 👀 | ✅ | components that render components |
|
||||
| CSS/Inline Styles | 🛠 | ✅ | syntax for inline/conditional styles |
|
||||
| 1st class global state | 🛠 | ✅ | redux/recoil/mobx on top of context |
|
||||
| ----- Phase 2 ----- | ----- | ----- | ----- |
|
||||
| 1st class router | 👀 | ✅ | Hook built on top of history |
|
||||
| Assets | 👀 | ✅ | include css/svg/img url statically |
|
||||
| Integrated classnames | 🛠 | 👀 | built-in `classnames` |
|
||||
| Suspense | 👀 | 👀 | schedule future render from future |
|
||||
| Transition | 👀 | 👀 | High-level control over suspense |
|
||||
| Animation | 👀 | ✅ | Spring-style animations |
|
||||
| Mobile | 👀 | ✅ | Render with cacao |
|
||||
| Desktop (native) | 👀 | ✅ | Render with native desktop |
|
||||
| 3D Renderer | 👀 | ✅ | react-three-fiber |
|
||||
| ----- Phase 3 ----- | ----- | ----- | ----- |
|
||||
| Portal | 👀 | ✅ | cast elements through tree |
|
||||
| Error/Panic boundary | 👀 | ✅ | catch panics and display BSOD |
|
||||
| Code-splitting | 👀 | ✅ | Make bundle smaller/lazy |
|
||||
| LiveView | 👀 | 👀 | Example for SSR + WASM apps |
|
||||
| Feature | Dioxus | React | Notes |
|
||||
| ---------------------- | ------ | ----- | ------------------------------------------------ |
|
||||
| ----- Phase 1 ----- | ----- | ----- | ----- |
|
||||
| Conditional Rendering | ✅ | ✅ | if/then to hide/show component |
|
||||
| Map, Iterator | ✅ | ✅ | map/filter/reduce rsx! |
|
||||
| Keyed Components | ✅ | ✅ | advanced diffing with keys |
|
||||
| Web | ✅ | ✅ | renderer for web browser |
|
||||
| Desktop (webview) | ✅ | ✅ | renderer for desktop |
|
||||
| Context | ✅ | ✅ | share state through the tree |
|
||||
| Hook | ✅ | ✅ | memory cells in components |
|
||||
| SSR | ✅ | ✅ | render directly to string |
|
||||
| Runs natively | ✅ | 👀 | runs as a sharable binary |
|
||||
| Component Children | ✅ | ✅ | ctx.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 |
|
||||
| NodeRef | 👀 | ✅ | gain direct access to nodes |
|
||||
| Controlled Inputs | 👀 | ✅ | stateful wrappers around inputs |
|
||||
| CSS/Inline Styles | 🛠 | ✅ | syntax for inline/conditional styles |
|
||||
| 1st class global state | 🛠 | ✅ | redux/recoil/mobx on top of context |
|
||||
| ----- Phase 2 ----- | ----- | ----- | ----- |
|
||||
| 1st class router | 👀 | ✅ | Hook built on top of history |
|
||||
| Assets | 👀 | ✅ | include css/svg/img url statically |
|
||||
| Integrated classnames | 🛠 | 👀 | built-in `classnames` |
|
||||
| Suspense | 👀 | 👀 | schedule future render from future |
|
||||
| Transition | 👀 | 👀 | High-level control over suspense |
|
||||
| Animation | 👀 | ✅ | Spring-style animations |
|
||||
| Mobile | 👀 | ✅ | Render with cacao |
|
||||
| Desktop (native) | 👀 | ✅ | Render with native desktop |
|
||||
| 3D Renderer | 👀 | ✅ | react-three-fiber |
|
||||
| ----- Phase 3 ----- | ----- | ----- | ----- |
|
||||
| Portal | 👀 | ✅ | cast elements through tree |
|
||||
| Error/Panic boundary | 👀 | ✅ | catch panics and display BSOD |
|
||||
| Code-splitting | 👀 | ✅ | Make bundle smaller/lazy |
|
||||
| LiveView | 👀 | 👀 | Example for SSR + WASM apps |
|
||||
|
||||
## Required services:
|
||||
|
||||
|
|
|
@ -50,15 +50,9 @@ impl Parse for AmbiguousElement {
|
|||
false => {
|
||||
let first_char = name_str.chars().next().unwrap();
|
||||
if first_char.is_ascii_uppercase() {
|
||||
if name_str == "Fragment" {
|
||||
input
|
||||
.parse::<Fragment>()
|
||||
.map(|c| AmbiguousElement::Fragment(c))
|
||||
} else {
|
||||
input
|
||||
.parse::<Component>()
|
||||
.map(|c| AmbiguousElement::Component(c))
|
||||
}
|
||||
input
|
||||
.parse::<Component>()
|
||||
.map(|c| AmbiguousElement::Component(c))
|
||||
} else {
|
||||
let name = input.parse::<Ident>().unwrap();
|
||||
Err(Error::new(
|
||||
|
|
|
@ -26,7 +26,7 @@ pub struct Component {
|
|||
// accept any path-like argument
|
||||
name: syn::Path,
|
||||
body: Vec<ComponentField>,
|
||||
_children: Vec<Node>,
|
||||
children: Vec<Node>,
|
||||
}
|
||||
|
||||
impl Parse for Component {
|
||||
|
@ -41,7 +41,7 @@ impl Parse for Component {
|
|||
syn::braced!(content in s);
|
||||
|
||||
let mut body: Vec<ComponentField> = Vec::new();
|
||||
let _children: Vec<Node> = Vec::new();
|
||||
let mut children: Vec<Node> = Vec::new();
|
||||
|
||||
'parsing: loop {
|
||||
// [1] Break if empty
|
||||
|
@ -49,7 +49,7 @@ impl Parse for Component {
|
|||
break 'parsing;
|
||||
}
|
||||
|
||||
if content.peek(token::Brace) {
|
||||
if content.peek(token::Brace) && content.peek2(Token![...]) {
|
||||
let inner: ParseBuffer;
|
||||
syn::braced!(inner in content);
|
||||
if inner.peek(Token![...]) {
|
||||
|
@ -57,7 +57,11 @@ impl Parse for Component {
|
|||
}
|
||||
}
|
||||
|
||||
body.push(content.parse::<ComponentField>()?);
|
||||
if content.peek(Ident) && content.peek2(Token![:]) {
|
||||
body.push(content.parse::<ComponentField>()?);
|
||||
} else {
|
||||
children.push(content.parse::<Node>()?);
|
||||
}
|
||||
|
||||
// consume comma if it exists
|
||||
// we don't actually care if there *are* commas between attrs
|
||||
|
@ -66,13 +70,10 @@ impl Parse for Component {
|
|||
}
|
||||
}
|
||||
|
||||
// todo: add support for children
|
||||
let children: Vec<Node> = vec![];
|
||||
|
||||
Ok(Self {
|
||||
name,
|
||||
body,
|
||||
_children: children,
|
||||
children,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -109,9 +110,32 @@ impl ToTokens for Component {
|
|||
None => quote! {None},
|
||||
};
|
||||
|
||||
let _toks = tokens.append_all(quote! {
|
||||
dioxus::builder::virtual_child(__ctx, #name, #builder, #key_token)
|
||||
});
|
||||
let childs = &self.children;
|
||||
let children = quote! {
|
||||
ChildrenList::new(__ctx)
|
||||
#( .add_child(#childs) )*
|
||||
.finish()
|
||||
};
|
||||
let name_str = name.segments.last().unwrap().ident.to_string();
|
||||
match name_str.as_str() {
|
||||
"Fragment" => tokens.append_all(quote! {
|
||||
dioxus::builder::vfragment(
|
||||
__ctx,
|
||||
#key_token,
|
||||
#children
|
||||
)
|
||||
}),
|
||||
_ => tokens.append_all(quote! {
|
||||
dioxus::builder::virtual_child(
|
||||
__ctx,
|
||||
#name,
|
||||
#builder,
|
||||
#key_token,
|
||||
#children
|
||||
)
|
||||
}),
|
||||
}
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -133,6 +133,10 @@ impl Parse for ElementAttr {
|
|||
}
|
||||
} else {
|
||||
match name_str.as_str() {
|
||||
"key" => {
|
||||
// todo: better error here
|
||||
AttrType::BumpText(s.parse::<LitStr>()?)
|
||||
}
|
||||
"style" => {
|
||||
//
|
||||
todo!("inline style not yet supported")
|
||||
|
@ -196,10 +200,23 @@ impl ToTokens for ElementAttr {
|
|||
let name = self.name.to_string();
|
||||
let nameident = &self.name;
|
||||
let _attr_stream = TokenStream2::new();
|
||||
|
||||
match &self.ty {
|
||||
AttrType::BumpText(value) => {
|
||||
AttrType::BumpText(value) => match name.as_str() {
|
||||
"key" => {
|
||||
tokens.append_all(quote! {
|
||||
.key2(format_args_f!(#value))
|
||||
});
|
||||
}
|
||||
_ => {
|
||||
tokens.append_all(quote! {
|
||||
.attr(#name, format_args_f!(#value))
|
||||
});
|
||||
}
|
||||
},
|
||||
AttrType::FieldTokens(exp) => {
|
||||
tokens.append_all(quote! {
|
||||
.attr(#name, format_args_f!(#value))
|
||||
.attr(#name, #exp)
|
||||
});
|
||||
}
|
||||
AttrType::Event(event) => {
|
||||
|
@ -207,16 +224,11 @@ impl ToTokens for ElementAttr {
|
|||
.add_listener(dioxus::events::on::#nameident(__ctx, #event))
|
||||
});
|
||||
}
|
||||
AttrType::FieldTokens(exp) => {
|
||||
tokens.append_all(quote! {
|
||||
.attr(#name, #exp)
|
||||
});
|
||||
}
|
||||
AttrType::EventTokens(event) => {
|
||||
//
|
||||
tokens.append_all(quote! {
|
||||
.add_listener(dioxus::events::on::#nameident(__ctx, #event))
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ static Header: FC<()> = |ctx| {
|
|||
ctx.render(dioxus::prelude::LazyNodes::new(|nodectx| {
|
||||
builder::ElementBuilder::new(nodectx, "div")
|
||||
.child(VNode::Component(nodectx.bump().alloc(VComponent::new(
|
||||
nodectx,
|
||||
Bottom,
|
||||
(),
|
||||
None,
|
||||
|
|
|
@ -7,21 +7,12 @@
|
|||
|
||||
use crate::innerlude::FC;
|
||||
|
||||
pub type ScopeIdx = generational_arena::Index;
|
||||
|
||||
pub unsafe trait Properties: PartialEq + Sized {
|
||||
type Builder;
|
||||
const CAN_BE_MEMOIZED: bool;
|
||||
fn builder() -> Self::Builder;
|
||||
}
|
||||
|
||||
pub struct EmptyBuilder;
|
||||
impl EmptyBuilder {
|
||||
pub fn build(self) -> () {
|
||||
()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Properties for () {
|
||||
const CAN_BE_MEMOIZED: bool = true;
|
||||
type Builder = EmptyBuilder;
|
||||
|
@ -31,151 +22,14 @@ unsafe impl Properties for () {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn fc_to_builder<T: Properties>(_f: FC<T>) -> T::Builder {
|
||||
pub struct EmptyBuilder;
|
||||
impl EmptyBuilder {
|
||||
#[inline]
|
||||
pub fn build(self) -> () {
|
||||
()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fc_to_builder<T: Properties>(_: FC<T>) -> T::Builder {
|
||||
T::builder()
|
||||
}
|
||||
|
||||
mod testing {
|
||||
use std::{any::Any, ops::Deref};
|
||||
|
||||
use crate::innerlude::VNode;
|
||||
|
||||
// trait PossibleProps {
|
||||
// type POut: PartialEq;
|
||||
// fn as_partial_eq(&self) -> Option<&Self::POut> {
|
||||
// None
|
||||
// }
|
||||
// }
|
||||
|
||||
// impl<T: PartialEq> PossibleProps for T {
|
||||
// type POut = Self;
|
||||
// }
|
||||
|
||||
// struct SomeProps2<'a> {
|
||||
// inner: &'a str,
|
||||
// }
|
||||
|
||||
// Fallback trait for to all types to default to `false`.
|
||||
trait NotEq {
|
||||
const IS_EQ: bool = false;
|
||||
}
|
||||
impl<T> NotEq for T {}
|
||||
|
||||
// Concrete wrapper type where `IS_COPY` becomes `true` if `T: Copy`.
|
||||
struct IsEq<G, T>(std::marker::PhantomData<(G, T)>);
|
||||
|
||||
impl<G: PartialEq, T: PartialEq<G>> IsEq<G, T> {
|
||||
// Because this is implemented directly on `IsCopy`, it has priority over
|
||||
// the `NotCopy` trait impl.
|
||||
//
|
||||
// Note: this is a *totally different* associated constant from that in
|
||||
// `NotCopy`. This does not specialize the `NotCopy` trait impl on `IsCopy`.
|
||||
const IS_EQ: bool = true;
|
||||
}
|
||||
|
||||
#[derive(PartialEq)]
|
||||
struct SomeProps {
|
||||
inner: &'static str,
|
||||
}
|
||||
|
||||
struct SomeProps2 {
|
||||
inner: &'static str,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
let g = IsEq::<SomeProps, SomeProps>::IS_EQ;
|
||||
|
||||
// let g = IsEq::<Vec<u32>>::IS_COPY;
|
||||
// let g = IsEq::<u32>::IS_COPY;
|
||||
// dbg!(g);
|
||||
|
||||
// let props = SomeProps { inner: "asd" };
|
||||
|
||||
// let intermediate: Box<dyn PartialEq<SomeProps>> = Box::new(props);
|
||||
// let as_any: Box<dyn Any> = Box::new(intermediate);
|
||||
|
||||
// let as_partialeq = as_any
|
||||
// .downcast_ref::<Box<dyn PartialEq<SomeProps>>>()
|
||||
// .unwrap();
|
||||
}
|
||||
|
||||
// struct blah {}
|
||||
// #[reorder_args]
|
||||
pub fn blah(a: i32, b: &str, c: &str) {}
|
||||
|
||||
// pub mod blah {
|
||||
// pub const a: u8 = 0;
|
||||
// pub const b: u8 = 1;
|
||||
// }
|
||||
|
||||
trait Eat {}
|
||||
impl Eat for fn() {}
|
||||
impl<T> Eat for fn(T) {}
|
||||
impl<T, K> Eat for fn(T, K) {}
|
||||
|
||||
mod other {
|
||||
use super::blah;
|
||||
fn test2() {
|
||||
// rsx!{
|
||||
// div {
|
||||
// Ele {
|
||||
// a: 10,
|
||||
// b: "asd"
|
||||
// c: impl Fn() -> ()
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// becomes
|
||||
|
||||
// const reorder: fn(_, _) = |a, b| {};
|
||||
// blah::META;
|
||||
// let a = 10;
|
||||
// let b = "asd";
|
||||
// let g = [10, 10.0];
|
||||
// let c = g.a;
|
||||
|
||||
// blah(10, "asd");
|
||||
}
|
||||
}
|
||||
|
||||
struct Inner<'a> {
|
||||
a: String,
|
||||
b: i32,
|
||||
c: &'a str,
|
||||
}
|
||||
|
||||
struct Custom<'a, P: 'a> {
|
||||
inner: &'a P,
|
||||
// inner: *const (),
|
||||
_p: std::marker::PhantomData<&'a P>,
|
||||
}
|
||||
|
||||
impl<'a, P> Custom<'a, P> {
|
||||
fn props(&self) -> &P {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
// impl<P> Deref for Custom<P> {
|
||||
// type Target = Inner;
|
||||
|
||||
// fn deref(&self) -> &Self::Target {
|
||||
// unsafe { &*self.inner }
|
||||
// }
|
||||
// }
|
||||
|
||||
fn test2<'a>(a: Custom<'a, Inner<'a>>) -> VNode {
|
||||
let r = a.inner;
|
||||
|
||||
todo!()
|
||||
// let g = a.props();
|
||||
// todo!()
|
||||
// let g = &a.a;
|
||||
}
|
||||
|
||||
fn is_comp() {}
|
||||
}
|
||||
|
||||
mod style {}
|
||||
|
|
|
@ -201,11 +201,13 @@ impl<'a> DiffMachine<'a> {
|
|||
todo!("Suspended components not currently available")
|
||||
}
|
||||
|
||||
// (VNode::Fragment(_), VNode::Fragment(_)) => {
|
||||
// todo!("Fragments not currently supported in diffing")
|
||||
// }
|
||||
// Fragments are special
|
||||
(VNode::Fragment(_), _) | (_, VNode::Fragment(_)) => {
|
||||
// we actually have to remove a bunch of nodes
|
||||
(VNode::Fragment(_), _) => {
|
||||
todo!("Fragments not currently supported in diffing")
|
||||
}
|
||||
|
||||
(_, VNode::Fragment(_)) => {
|
||||
todo!("Fragments not currently supported in diffing")
|
||||
}
|
||||
}
|
||||
|
@ -227,7 +229,7 @@ impl<'a> DiffMachine<'a> {
|
|||
self.change_list.create_text_node(text);
|
||||
}
|
||||
VNode::Element(&VElement {
|
||||
key: _,
|
||||
key,
|
||||
tag_name,
|
||||
listeners,
|
||||
attributes,
|
||||
|
@ -266,7 +268,12 @@ impl<'a> DiffMachine<'a> {
|
|||
|
||||
for child in children {
|
||||
self.create(child);
|
||||
self.change_list.append_child();
|
||||
if let VNode::Fragment(_) = child {
|
||||
// do nothing
|
||||
// fragments append themselves
|
||||
} else {
|
||||
self.change_list.append_child();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -284,22 +291,23 @@ impl<'a> DiffMachine<'a> {
|
|||
// those references are stable, even if the component arena moves around in memory, thanks to the bump arenas.
|
||||
// However, there is no way to convey this to rust, so we need to use unsafe to pierce through the lifetime.
|
||||
|
||||
let parent_idx = self.cur_idx;
|
||||
|
||||
// Insert a new scope into our component list
|
||||
let idx = self
|
||||
.components
|
||||
.with(|components| {
|
||||
components.insert_with(|new_idx| {
|
||||
let cur_idx = self.cur_idx;
|
||||
let cur_scope = self.components.try_get(cur_idx).unwrap();
|
||||
let height = cur_scope.height + 1;
|
||||
let parent_scope = self.components.try_get(parent_idx).unwrap();
|
||||
let height = parent_scope.height + 1;
|
||||
Scope::new(
|
||||
caller,
|
||||
new_idx,
|
||||
Some(cur_idx),
|
||||
Some(parent_idx),
|
||||
height,
|
||||
self.event_queue.new_channel(height, new_idx),
|
||||
self.components.clone(),
|
||||
&[],
|
||||
component.children,
|
||||
)
|
||||
})
|
||||
})
|
||||
|
@ -323,24 +331,24 @@ impl<'a> DiffMachine<'a> {
|
|||
// Run the scope for one iteration to initialize it
|
||||
new_component.run_scope().unwrap();
|
||||
|
||||
// // Navigate the diff machine to the right point in the output dom
|
||||
// self.change_list.load_known_root(id);
|
||||
// let root_id = component.stable_addr
|
||||
|
||||
// And then run the diff algorithm
|
||||
self.diff_node(new_component.old_frame(), new_component.next_frame());
|
||||
|
||||
// Finally, insert this node as a seen node.
|
||||
self.seen_nodes.insert(idx);
|
||||
}
|
||||
VNode::Suspended => {
|
||||
todo!("Creation of VNode::Suspended not yet supported")
|
||||
}
|
||||
|
||||
// we go the the "known root" but only operate on a sibling basis
|
||||
VNode::Fragment(frag) => {
|
||||
//
|
||||
todo!("Cannot current create fragments")
|
||||
// create the children directly in the space
|
||||
for child in frag.children {
|
||||
self.create(child);
|
||||
self.change_list.append_child();
|
||||
}
|
||||
}
|
||||
|
||||
VNode::Suspended => {
|
||||
todo!("Creation of VNode::Suspended not yet supported")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -87,8 +87,6 @@ pub mod builder {
|
|||
pub use super::nodebuilder::*;
|
||||
}
|
||||
|
||||
pub mod support;
|
||||
|
||||
// types used internally that are important
|
||||
pub(crate) mod innerlude {
|
||||
pub use crate::component::*;
|
||||
|
@ -122,14 +120,12 @@ pub mod prelude {
|
|||
|
||||
pub use crate::nodebuilder::LazyNodes;
|
||||
|
||||
pub use crate::nodebuilder::ChildrenList;
|
||||
pub use crate::virtual_dom::NodeCtx;
|
||||
// pub use nodes::iterables::IterableNodes;
|
||||
/// This type alias is an internal way of abstracting over the static functions that represent components.
|
||||
pub use crate::innerlude::FC;
|
||||
|
||||
// TODO @Jon, fix this
|
||||
// hack the VNode type until VirtualNode is fixed in the macro crate
|
||||
|
||||
// expose our bumpalo type
|
||||
pub use bumpalo;
|
||||
pub use bumpalo::Bump;
|
||||
|
@ -141,19 +137,9 @@ pub mod prelude {
|
|||
|
||||
pub use dioxus_core_macro::{format_args_f, html, rsx, Props};
|
||||
|
||||
pub use crate::component::ScopeIdx;
|
||||
pub use crate::diff::DiffMachine;
|
||||
pub use crate::virtual_dom::ScopeIdx;
|
||||
|
||||
pub use crate::debug_renderer::DebugRenderer;
|
||||
pub use crate::dioxus_main;
|
||||
pub use crate::hooks::*;
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! dioxus_main {
|
||||
($i:ident) => {
|
||||
fn main() {
|
||||
todo!("this macro is a placeholder for launching a dioxus app on different platforms. \nYou probably don't want to use this, but it's okay for small apps.")
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
//! Helpers for building virtual DOM VNodes.
|
||||
|
||||
use std::{any::Any, borrow::BorrowMut, intrinsics::transmute, u128};
|
||||
use std::{any::Any, borrow::BorrowMut, fmt::Arguments, intrinsics::transmute, u128};
|
||||
|
||||
use crate::{
|
||||
events::VirtualEvent,
|
||||
innerlude::{Properties, VComponent, FC},
|
||||
nodes::{Attribute, Listener, NodeKey, VNode},
|
||||
prelude::VElement,
|
||||
prelude::{VElement, VFragment},
|
||||
virtual_dom::NodeCtx,
|
||||
};
|
||||
|
||||
|
@ -269,6 +269,20 @@ where
|
|||
self
|
||||
}
|
||||
|
||||
pub fn key2(mut self, args: Arguments) -> Self {
|
||||
let key = match args.as_str() {
|
||||
Some(static_str) => static_str,
|
||||
None => {
|
||||
use bumpalo::core_alloc::fmt::Write;
|
||||
let mut s = bumpalo::collections::String::new_in(self.ctx.bump());
|
||||
s.write_fmt(args).unwrap();
|
||||
s.into_bump_str()
|
||||
}
|
||||
};
|
||||
self.key = NodeKey(Some(key));
|
||||
self
|
||||
}
|
||||
|
||||
/// Create the virtual DOM VNode described by this builder.
|
||||
///
|
||||
/// # Example
|
||||
|
@ -655,14 +669,49 @@ pub fn virtual_child<'a, T: Properties + 'a>(
|
|||
f: FC<T>,
|
||||
props: T,
|
||||
key: Option<&'a str>, // key: NodeKey<'a>,
|
||||
children: &'a [VNode<'a>],
|
||||
) -> VNode<'a> {
|
||||
// currently concerned about if props have a custom drop implementation
|
||||
// might override it with the props macro
|
||||
// todo!()
|
||||
VNode::Component(
|
||||
ctx.bump()
|
||||
.alloc(crate::nodes::VComponent::new(ctx.bump(), f, props, key)),
|
||||
.alloc(crate::nodes::VComponent::new(ctx, f, props, key, children)),
|
||||
// ctx.bump()
|
||||
// .alloc(crate::nodes::VComponent::new(f, props, key)),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn vfragment<'a>(
|
||||
ctx: &NodeCtx<'a>,
|
||||
key: Option<&'a str>, // key: NodeKey<'a>,
|
||||
children: &'a [VNode<'a>],
|
||||
) -> VNode<'a> {
|
||||
VNode::Fragment(ctx.bump().alloc(VFragment::new(key, children)))
|
||||
}
|
||||
|
||||
pub struct ChildrenList<'a, 'b> {
|
||||
ctx: &'b NodeCtx<'a>,
|
||||
children: bumpalo::collections::Vec<'a, VNode<'a>>,
|
||||
}
|
||||
|
||||
impl<'a, 'b> ChildrenList<'a, 'b> {
|
||||
pub fn new(ctx: &'b NodeCtx<'a>) -> Self {
|
||||
Self {
|
||||
ctx,
|
||||
children: bumpalo::collections::Vec::new_in(ctx.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.ctx);
|
||||
self.children.push(child);
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
pub fn finish(self) -> &'a [VNode<'a>] {
|
||||
self.children.into_bump_slice()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,8 @@ use crate::{
|
|||
events::VirtualEvent,
|
||||
innerlude::{Context, Properties, Scope, ScopeIdx, FC},
|
||||
nodebuilder::text3,
|
||||
virtual_dom::NodeCtx,
|
||||
// support::NodeCtx,
|
||||
};
|
||||
use bumpalo::Bump;
|
||||
use std::{
|
||||
|
@ -238,7 +240,6 @@ pub struct VComponent<'src> {
|
|||
|
||||
// a pointer to the raw fn typ
|
||||
pub user_fc: *const (),
|
||||
_p: PhantomData<&'src ()>,
|
||||
}
|
||||
|
||||
impl<'a> VComponent<'a> {
|
||||
|
@ -249,14 +250,17 @@ impl<'a> VComponent<'a> {
|
|||
// we want them to borrow references... maybe force implementing a "to_static_unsafe" trait
|
||||
|
||||
pub fn new<P: Properties + 'a>(
|
||||
bump: &'a Bump,
|
||||
// bump: &'a Bump,
|
||||
ctx: &NodeCtx<'a>,
|
||||
component: FC<P>,
|
||||
// props: bumpalo::boxed::Box<'a, P>,
|
||||
props: P,
|
||||
key: Option<&'a str>,
|
||||
children: &'a [VNode<'a>],
|
||||
) -> Self {
|
||||
// pub fn new<P: Properties + 'a>(component: FC<P>, props: P, key: Option<&'a str>) -> Self {
|
||||
// let bad_props = unsafe { transmogrify(props) };
|
||||
let bump = ctx.bump();
|
||||
let caller_ref = component as *const ();
|
||||
let props = bump.alloc(props);
|
||||
|
||||
|
@ -319,8 +323,7 @@ impl<'a> VComponent<'a> {
|
|||
user_fc: caller_ref,
|
||||
comparator,
|
||||
raw_props,
|
||||
_p: PhantomData,
|
||||
children: &[],
|
||||
children,
|
||||
caller,
|
||||
stable_addr: RefCell::new(None),
|
||||
}
|
||||
|
@ -355,3 +358,14 @@ pub struct VFragment<'src> {
|
|||
pub key: NodeKey<'src>,
|
||||
pub children: &'src [VNode<'src>],
|
||||
}
|
||||
|
||||
impl<'a> VFragment<'a> {
|
||||
pub fn new(key: Option<&'a str>, children: &'a [VNode<'a>]) -> Self {
|
||||
let key = match key {
|
||||
Some(key) => NodeKey::new(key),
|
||||
None => NodeKey(None),
|
||||
};
|
||||
|
||||
Self { key, children }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,165 +0,0 @@
|
|||
use crate::{arena::ScopeArena, innerlude::*};
|
||||
use bumpalo::Bump;
|
||||
use generational_arena::Arena;
|
||||
use std::{
|
||||
any::{Any, TypeId},
|
||||
cell::RefCell,
|
||||
collections::{HashMap, HashSet, VecDeque},
|
||||
fmt::Debug,
|
||||
future::Future,
|
||||
ops::Deref,
|
||||
pin::Pin,
|
||||
rc::{Rc, Weak},
|
||||
};
|
||||
// We actually allocate the properties for components in their parent's properties
|
||||
// We then expose a handle to use those props for render in the form of "OpaqueComponent"
|
||||
pub(crate) type OpaqueComponent<'e> = dyn Fn(&'e Scope) -> VNode<'e> + 'e;
|
||||
// pub(crate) type OpaqueComponent<'e> = dyn for<'b> Fn(&'b Scope) -> VNode<'b> + 'e;
|
||||
|
||||
#[derive(PartialEq, Debug, Clone, Default)]
|
||||
pub(crate) struct EventQueue(pub Rc<RefCell<Vec<HeightMarker>>>);
|
||||
|
||||
impl EventQueue {
|
||||
pub fn new_channel(&self, height: u32, idx: ScopeIdx) -> Rc<dyn Fn()> {
|
||||
let inner = self.clone();
|
||||
let marker = HeightMarker { height, idx };
|
||||
Rc::new(move || inner.0.as_ref().borrow_mut().push(marker))
|
||||
}
|
||||
}
|
||||
|
||||
/// A helper type that lets scopes be ordered by their height
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub(crate) struct HeightMarker {
|
||||
pub idx: ScopeIdx,
|
||||
pub height: u32,
|
||||
}
|
||||
|
||||
impl Ord for HeightMarker {
|
||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||
self.height.cmp(&other.height)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for HeightMarker {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
// NodeCtx 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)]
|
||||
pub struct NodeCtx<'a> {
|
||||
pub scope_ref: &'a Scope,
|
||||
pub listener_id: RefCell<usize>,
|
||||
}
|
||||
|
||||
impl<'a> NodeCtx<'a> {
|
||||
pub fn bump(&self) -> &'a Bump {
|
||||
&self.scope_ref.cur_frame().bump
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for NodeCtx<'_> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Hash)]
|
||||
pub struct ContextId {
|
||||
// Which component is the scope in
|
||||
original: ScopeIdx,
|
||||
|
||||
// What's the height of the scope
|
||||
height: u32,
|
||||
|
||||
// Which scope is it (in order)
|
||||
id: u32,
|
||||
}
|
||||
|
||||
pub struct ActiveFrame {
|
||||
// We use a "generation" for users of contents in the bump frames to ensure their data isn't broken
|
||||
pub generation: RefCell<usize>,
|
||||
|
||||
// The double-buffering situation that we will use
|
||||
pub frames: [BumpFrame; 2],
|
||||
}
|
||||
|
||||
pub struct BumpFrame {
|
||||
pub bump: Bump,
|
||||
pub head_node: VNode<'static>,
|
||||
}
|
||||
|
||||
impl ActiveFrame {
|
||||
pub fn new() -> Self {
|
||||
Self::from_frames(
|
||||
BumpFrame {
|
||||
bump: Bump::new(),
|
||||
head_node: VNode::text(""),
|
||||
},
|
||||
BumpFrame {
|
||||
bump: Bump::new(),
|
||||
head_node: VNode::text(""),
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn from_frames(a: BumpFrame, b: BumpFrame) -> Self {
|
||||
Self {
|
||||
generation: 0.into(),
|
||||
frames: [a, b],
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn cur_frame(&self) -> &BumpFrame {
|
||||
match *self.generation.borrow() & 1 == 0 {
|
||||
true => &self.frames[0],
|
||||
false => &self.frames[1],
|
||||
}
|
||||
}
|
||||
pub(crate) fn cur_frame_mut(&mut self) -> &mut BumpFrame {
|
||||
match *self.generation.borrow() & 1 == 0 {
|
||||
true => &mut self.frames[0],
|
||||
false => &mut self.frames[1],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn current_head_node<'b>(&'b self) -> &'b VNode<'b> {
|
||||
let raw_node = match *self.generation.borrow() & 1 == 0 {
|
||||
true => &self.frames[0],
|
||||
false => &self.frames[1],
|
||||
};
|
||||
|
||||
// Give out our self-referential item with our own borrowed lifetime
|
||||
unsafe {
|
||||
let unsafe_head = &raw_node.head_node;
|
||||
let safe_node = std::mem::transmute::<&VNode<'static>, &VNode<'b>>(unsafe_head);
|
||||
safe_node
|
||||
}
|
||||
}
|
||||
|
||||
pub fn prev_head_node<'b>(&'b self) -> &'b VNode<'b> {
|
||||
let raw_node = match *self.generation.borrow() & 1 != 0 {
|
||||
true => &self.frames[0],
|
||||
false => &self.frames[1],
|
||||
};
|
||||
|
||||
// Give out our self-referential item with our own borrowed lifetime
|
||||
unsafe {
|
||||
let unsafe_head = &raw_node.head_node;
|
||||
let safe_node = std::mem::transmute::<&VNode<'static>, &VNode<'b>>(unsafe_head);
|
||||
safe_node
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn next(&mut self) -> &mut BumpFrame {
|
||||
*self.generation.borrow_mut() += 1;
|
||||
|
||||
if *self.generation.borrow() % 2 == 0 {
|
||||
&mut self.frames[0]
|
||||
} else {
|
||||
&mut self.frames[1]
|
||||
}
|
||||
}
|
||||
}
|
|
@ -32,6 +32,7 @@ use std::{
|
|||
pin::Pin,
|
||||
rc::{Rc, Weak},
|
||||
};
|
||||
pub type ScopeIdx = generational_arena::Index;
|
||||
|
||||
/// An integrated virtual node system that progresses events and diffs UI trees.
|
||||
/// Differences are converted into patches which a renderer can use to draw the UI.
|
||||
|
@ -420,8 +421,10 @@ impl Scope {
|
|||
>(caller)
|
||||
};
|
||||
|
||||
let child_nodes = unsafe { std::mem::transmute(child_nodes) };
|
||||
|
||||
Self {
|
||||
child_nodes: &[],
|
||||
child_nodes: child_nodes,
|
||||
caller,
|
||||
parent,
|
||||
arena_idx,
|
||||
|
|
|
@ -84,7 +84,7 @@ fn use_map() {}
|
|||
// Elements are received as Rc<T> in case the underlying collection is shuffled around
|
||||
// Setters/getters can be generated
|
||||
fn use_collection<'a, T: Collection>(
|
||||
ctx: &Context<'a>,
|
||||
ctx: &impl Scoped<'a>,
|
||||
f: impl Fn() -> T,
|
||||
) -> CollectionHandle<'a, T> {
|
||||
ctx.use_hook(
|
||||
|
|
|
@ -20,7 +20,7 @@ fn update_title(api: &RecoilApi) {
|
|||
}
|
||||
|
||||
static App: FC<()> = |ctx| {
|
||||
let title = use_read(ctx, &TITLE);
|
||||
let title = use_read(&ctx, &TITLE);
|
||||
let next_light = use_recoil_api(ctx, |api| move |_| update_title(&api));
|
||||
|
||||
rsx! { in ctx,
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
//! Basic example that renders a simple VNode to the browser.
|
||||
|
||||
use dioxus_core::prelude::*;
|
||||
use dioxus_web::*;
|
||||
|
||||
fn main() {
|
||||
// Setup logging
|
||||
wasm_logger::init(wasm_logger::Config::new(log::Level::Debug));
|
||||
console_error_panic_hook::set_once();
|
||||
|
||||
// Run the app
|
||||
wasm_bindgen_futures::spawn_local(WebsysRenderer::start(App));
|
||||
}
|
||||
|
||||
static App: FC<()> = |ctx| {
|
||||
ctx.render(rsx! {
|
||||
Calcier {
|
||||
h2 {"abc 1"}
|
||||
h2 {"abc 2"}
|
||||
h2 {"abc 3"}
|
||||
h2 {"abc 4"}
|
||||
h2 {"abc 5"}
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
static Calcier: FC<()> = |ctx| {
|
||||
ctx.render(rsx! {
|
||||
div {
|
||||
h1 {
|
||||
"abc 0"
|
||||
}
|
||||
{ctx.children()}
|
||||
}
|
||||
})
|
||||
};
|
|
@ -52,7 +52,7 @@ struct ButtonProps {
|
|||
id: u8,
|
||||
}
|
||||
|
||||
fn CustomButton(ctx: Context, props: &ButtonProps) -> VNode {
|
||||
fn CustomButton(ctx: Context<ButtonProps>) -> VNode {
|
||||
let names = ctx.use_context::<CustomContext>();
|
||||
let name = names.0[ctx.id as usize];
|
||||
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
//! Basic example that renders a simple VNode to the browser.
|
||||
|
||||
use dioxus_core::prelude::*;
|
||||
use dioxus_web::*;
|
||||
|
||||
fn main() {
|
||||
// Setup logging
|
||||
wasm_logger::init(wasm_logger::Config::new(log::Level::Debug));
|
||||
console_error_panic_hook::set_once();
|
||||
|
||||
// Run the app
|
||||
wasm_bindgen_futures::spawn_local(WebsysRenderer::start(App));
|
||||
}
|
||||
|
||||
static App: FC<()> = |ctx| {
|
||||
ctx.render(rsx! {
|
||||
h2 { "abc 1" }
|
||||
h2 { "abc 1" }
|
||||
h2 { "abc 1" }
|
||||
h2 { "abc 1" }
|
||||
h2 { "abc 1" }
|
||||
h2 { "abc 1" }
|
||||
div {
|
||||
"hello world!"
|
||||
}
|
||||
})
|
||||
};
|
|
@ -0,0 +1,21 @@
|
|||
use dioxus_core::prelude::*;
|
||||
use dioxus_web::WebsysRenderer;
|
||||
|
||||
fn main() {
|
||||
wasm_logger::init(wasm_logger::Config::new(log::Level::Debug));
|
||||
console_error_panic_hook::set_once();
|
||||
|
||||
log::info!("hello world");
|
||||
wasm_bindgen_futures::spawn_local(WebsysRenderer::start(Example));
|
||||
}
|
||||
|
||||
static Example: FC<()> = |ctx| {
|
||||
ctx.render(rsx! {
|
||||
div {
|
||||
span {
|
||||
class: "px-2 py-1 flex w-36 mt-4 items-center text-xs rounded-md font-semibold text-yellow-500 bg-yellow-100"
|
||||
"DUE DATE : 18 JUN"
|
||||
}
|
||||
}
|
||||
})
|
||||
};
|
|
@ -53,7 +53,7 @@ struct ButtonProps<'src, F: Fn(MouseEvent)> {
|
|||
handler: F
|
||||
}
|
||||
|
||||
fn CustomButton<'b, 'a, F: Fn(MouseEvent)>(ctx: Context<'a>, props: &'b ButtonProps<'b, F>) -> VNode {
|
||||
fn CustomButton<'a, F: Fn(MouseEvent)>(ctx: Context<'a, ButtonProps<'a, F>>) -> VNode {
|
||||
ctx.render(rsx!{
|
||||
button {
|
||||
class: "inline-block py-4 px-8 mr-6 leading-none text-white bg-indigo-600 hover:bg-indigo-900 font-semibold rounded shadow"
|
||||
|
@ -74,7 +74,7 @@ impl<F: Fn(MouseEvent)> PartialEq for ButtonProps<'_, F> {
|
|||
struct PlaceholderProps {
|
||||
val: &'static str
|
||||
}
|
||||
fn Placeholder(ctx: Context, props: &PlaceholderProps) -> VNode {
|
||||
fn Placeholder(ctx: Context<PlaceholderProps>) -> VNode {
|
||||
ctx.render(rsx!{
|
||||
div {
|
||||
"child: {ctx.val}"
|
||||
|
|
Loading…
Reference in New Issue