wip: more progress on parity docs.
Placeholders in the rsx macro for future features.
This commit is contained in:
parent
4d5c528b07
commit
c5089ba3c5
|
@ -30,3 +30,8 @@ partialeq
|
|||
rsx
|
||||
Ctx
|
||||
fmt
|
||||
Reqwest
|
||||
Gloo
|
||||
mobx
|
||||
img
|
||||
svg
|
||||
|
|
|
@ -2,33 +2,65 @@
|
|||
|
||||
Sorted by priority
|
||||
|
||||
| Feature | Dioxus | React |
|
||||
| ---------------------- | ------ | ----- |
|
||||
| Conditional Rendering | ✅ | ✅ |
|
||||
| Map, Iterator | ✅ | ✅ |
|
||||
| Keyed Components | ✅ | ✅ |
|
||||
| Web | ✅ | ✅ |
|
||||
| Desktop (webview) | ✅ | ✅ |
|
||||
| Context | ✅ | ✅ |
|
||||
| Hook | ✅ | ✅ |
|
||||
| SSR | ✅ | ✅ |
|
||||
| Runs natively | ✅ | 👀 |
|
||||
| Null components | 👀 | ✅ |
|
||||
| Fragments | 👀 | ✅ |
|
||||
| Component Children | 👀 | ✅ |
|
||||
| NodeRef | 👀 | ✅ |
|
||||
| Controlled Inputs | 👀 | ✅ |
|
||||
| No-div components | 👀 | ✅ |
|
||||
| CSS/Inline Styles | 👀 | ✅ |
|
||||
| 1st class global state | 👀 | ✅ |
|
||||
| -------------------- | ----- | ----- |
|
||||
| 1st class router | 👀 | ✅ |
|
||||
| Suspense | 👀 | 👀 |
|
||||
| Animation | 👀 | ✅ |
|
||||
| Mobile | 👀 | ✅ |
|
||||
| Desktop (native) | 👀 | ✅ |
|
||||
| 3D Renderer | 👀 | ✅ |
|
||||
| -------------------- | ----- | ----- |
|
||||
| Portal | 👀 | ✅ |
|
||||
| Error boundary | 👀 | ✅ |
|
||||
| Code-splitting | 👀 | ✅ |
|
||||
| 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 |
|
||||
|
||||
## Required services:
|
||||
|
||||
---
|
||||
|
||||
Gloo is covering a lot of these. We want to build hooks around these, and provide examples on how to use them.
|
||||
https://github.com/rustwasm/gloo
|
||||
|
||||
If the gloo service doesn't exist, then we need to contribute to the project
|
||||
|
||||
| Service | Hook examples | Current Projects |
|
||||
| ---------------------------- | ------------- | ---------------- |
|
||||
| Fetch | 👀 | Reqwest/surf |
|
||||
| Local storage (cache) | 👀 | Gloo |
|
||||
| Persistent storage (IndexDB) | 👀 | 👀 |
|
||||
| WebSocket | 👀 | Gloo |
|
||||
| 3D Renderer / WebGL | 👀 | Gloo |
|
||||
| Web Worker | 👀 | 👀 |
|
||||
| Router | 👀 | 👀 |
|
||||
| Notifications | 👀 | 👀 |
|
||||
| WebRTC Client | 👀 | 👀 |
|
||||
| Service Workers | 👀 | 👀 |
|
||||
| Resize Observer | 👀 | 👀 |
|
||||
| Canvas | 👀 | 👀 |
|
||||
| Clipboard | 👀 | 👀 |
|
||||
| Fullscreen | 👀 | 👀 |
|
||||
| History API | 👀 | 👀 |
|
||||
|
|
|
@ -213,6 +213,14 @@ impl Parse for Component {
|
|||
break 'parsing;
|
||||
}
|
||||
|
||||
if content.peek(token::Brace) {
|
||||
let inner: ParseBuffer;
|
||||
syn::braced!(inner in content);
|
||||
if inner.peek(Token![...]) {
|
||||
todo!("Inline props not yet supported");
|
||||
}
|
||||
}
|
||||
|
||||
body.push(content.parse::<ComponentField>()?);
|
||||
|
||||
// consume comma if it exists
|
||||
|
@ -329,7 +337,7 @@ impl Parse for Element {
|
|||
let forked = content.fork();
|
||||
if forked.call(Ident::parse_any).is_ok()
|
||||
&& forked.parse::<Token![:]>().is_ok()
|
||||
&& forked.parse::<Expr>().is_ok()
|
||||
&& forked.parse::<Token![:]>().is_err()
|
||||
{
|
||||
attrs.push(content.parse::<ElementAttr>()?);
|
||||
} else {
|
||||
|
@ -386,7 +394,7 @@ struct ElementAttr {
|
|||
}
|
||||
|
||||
enum AttrType {
|
||||
Value(LitStr),
|
||||
BumpText(LitStr),
|
||||
FieldTokens(Expr),
|
||||
EventTokens(Expr),
|
||||
Event(ExprClosure),
|
||||
|
@ -420,14 +428,34 @@ impl Parse for ElementAttr {
|
|||
AttrType::Event(s.parse()?)
|
||||
}
|
||||
} else {
|
||||
let fork = s.fork();
|
||||
if let Ok(rawtext) = fork.parse::<LitStr>() {
|
||||
s.advance_to(&fork);
|
||||
AttrType::Value(rawtext)
|
||||
} else {
|
||||
let toks = s.parse::<Expr>()?;
|
||||
AttrType::FieldTokens(toks)
|
||||
match name_str.as_str() {
|
||||
"style" => {
|
||||
//
|
||||
todo!("inline style not yet supported")
|
||||
}
|
||||
"classes" => {
|
||||
//
|
||||
todo!("custom class lsit not supported")
|
||||
}
|
||||
"namespace" => {
|
||||
//
|
||||
todo!("custom namespace not supported")
|
||||
}
|
||||
"ref" => {
|
||||
//
|
||||
todo!("custom ref not supported")
|
||||
}
|
||||
_ => {
|
||||
if s.peek(LitStr) {
|
||||
let rawtext = s.parse::<LitStr>().unwrap();
|
||||
AttrType::BumpText(rawtext)
|
||||
} else {
|
||||
let toks = s.parse::<Expr>()?;
|
||||
AttrType::FieldTokens(toks)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// let lit_str = if name_str == "style" && s.peek(token::Brace) {
|
||||
// // special-case to deal with literal styles.
|
||||
// let outer;
|
||||
|
@ -465,7 +493,7 @@ impl ToTokens for &ElementAttr {
|
|||
let nameident = &self.name;
|
||||
let _attr_stream = TokenStream2::new();
|
||||
match &self.ty {
|
||||
AttrType::Value(value) => {
|
||||
AttrType::BumpText(value) => {
|
||||
tokens.append_all(quote! {
|
||||
.attr(#name, {
|
||||
use bumpalo::core_alloc::fmt::Write;
|
||||
|
|
|
@ -97,7 +97,7 @@ static VALID_TAGS: Lazy<HashSet<&'static str>> = Lazy::new(|| {
|
|||
"source",
|
||||
"span",
|
||||
"strong",
|
||||
"style",
|
||||
// "style",
|
||||
"sub",
|
||||
"summary",
|
||||
"sup",
|
||||
|
|
|
@ -5,10 +5,11 @@ This is the core crate for the Dioxus Virtual DOM. This README will focus on the
|
|||
We reserve the "dioxus" name and aggregate all the various renderers under it. If you want just a single dioxus renderer, then chose from "dioxus-web", "dioxus-desktop", etc.
|
||||
|
||||
## Internals
|
||||
|
||||
Dioxus-core builds off the many frameworks that came before it. Notably, Dioxus borrows these concepts:
|
||||
|
||||
- React: hooks, concurrency, suspense
|
||||
- Dodrio: bump allocation, double buffering, and source code for nodes + NodeBuilder
|
||||
- Dodrio: bump allocation, double buffering, and source code for NodeBuilder
|
||||
- Percy: html! macro architecture, platform-agnostic edits
|
||||
- Yew: passion and inspiration ❤️
|
||||
|
||||
|
@ -28,7 +29,7 @@ We have big goals for Dioxus. The final implementation must:
|
|||
|
||||
- Support a pluggable allocation strategy that makes VNode creation **very** fast
|
||||
- Support lazy DomTrees (ie DomTrees that are not actually created when the html! macro is used)
|
||||
- Support advanced diffing strategies (patience, Myers, etc)
|
||||
- Support advanced diffing strategies (patience, Meyers, etc)
|
||||
|
||||
## Design Quirks
|
||||
|
||||
|
|
|
@ -12,7 +12,10 @@ fn main() {
|
|||
}
|
||||
baller::Baller {}
|
||||
baller::Baller {}
|
||||
Baller {}
|
||||
Baller {
|
||||
// todo: manual props
|
||||
// {...BallerProps {}}
|
||||
}
|
||||
div {
|
||||
a: "asd",
|
||||
a: "asd",
|
||||
|
@ -23,11 +26,12 @@ fn main() {
|
|||
"asdas",
|
||||
"asdas",
|
||||
"asdas",
|
||||
div {},
|
||||
div {
|
||||
|
||||
},
|
||||
div {
|
||||
|
||||
// classes: {[ ("baller", true), ("maller", false) ]}
|
||||
// class: "asdasd"
|
||||
// class: "{customname}",
|
||||
// class: {[("baller", true), ("hover", false)]}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -50,5 +54,6 @@ pub struct TallerProps {
|
|||
}
|
||||
|
||||
pub fn Taller(ctx: Context, props: &TallerProps) -> DomTree {
|
||||
let b = true;
|
||||
todo!()
|
||||
}
|
||||
|
|
|
@ -1,123 +0,0 @@
|
|||
use crate::{innerlude::*, nodebuilder::IntoDomTree};
|
||||
use crate::{nodebuilder::LazyNodes, nodes::VNode};
|
||||
use bumpalo::Bump;
|
||||
use std::{cell::RefCell, future::Future, ops::Deref, pin::Pin, rc::Rc, sync::atomic::AtomicUsize};
|
||||
|
||||
/// Context API
|
||||
///
|
||||
/// The context API provides a mechanism for components to borrow state from other components higher in the tree.
|
||||
/// By combining the Context API and the Subscription API, we can craft ergonomic global state management systems.
|
||||
///
|
||||
/// This API is inherently dangerous because we could easily cause UB by allowing &T and &mut T to exist at the same time.
|
||||
/// To prevent this, we expose the RemoteState<T> and RemoteLock<T> types which act as a form of reverse borrowing.
|
||||
/// This is very similar to RwLock, except that RemoteState is copy-able. Unlike RwLock, derefing RemoteState can
|
||||
/// cause panics if the pointer is null. In essence, we sacrifice the panic protection for ergonomics, but arrive at
|
||||
/// a similar end result.
|
||||
///
|
||||
/// Instead of placing the onus on the receiver of the data to use it properly, we wrap the source object in a
|
||||
/// "shield" where gaining &mut access can only be done if no active StateGuards are open. This would fail and indicate
|
||||
/// a failure of implementation.
|
||||
|
||||
pub struct RemoteState<T> {
|
||||
inner: *const T,
|
||||
}
|
||||
impl<T> Copy for RemoteState<T> {}
|
||||
|
||||
impl<T> Clone for RemoteState<T> {
|
||||
fn clone(&self) -> Self {
|
||||
Self { inner: self.inner }
|
||||
}
|
||||
}
|
||||
|
||||
static DEREF_ERR_MSG: &'static str = r#"""
|
||||
[ERROR]
|
||||
This state management implementation is faulty. Report an issue on whatever implementation is using this.
|
||||
Context should *never* be dangling!. If a Context is torn down, so should anything that references it.
|
||||
"""#;
|
||||
|
||||
impl<T> Deref for RemoteState<T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
// todo!
|
||||
// Try to borrow the underlying context manager, register this borrow with the manager as a "weak" subscriber.
|
||||
// This will prevent the panic and ensure the pointer still exists.
|
||||
// For now, just get an immutable reference to the underlying context data.
|
||||
//
|
||||
// It's important to note that ContextGuard is not a public API, and can only be made from UseContext.
|
||||
// This guard should only be used in components, and never stored in hooks
|
||||
unsafe {
|
||||
match self.inner.as_ref() {
|
||||
Some(ptr) => ptr,
|
||||
None => panic!(DEREF_ERR_MSG),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// impl<'a> crate::virtual_dom::Context<'a> {
|
||||
// // impl<'a, P> super::Context<'a, P> {
|
||||
// pub fn use_context<I, O>(&'a self, _narrow: impl Fn(&'_ I) -> &'_ O) -> RemoteState<O> {
|
||||
// todo!()
|
||||
// }
|
||||
|
||||
// pub fn create_context<T: 'static>(&self, creator: impl FnOnce() -> T) {}
|
||||
// }
|
||||
|
||||
/// # SAFETY ALERT
|
||||
///
|
||||
/// The underlying context mechanism relies on mutating &mut T while &T is held by components in the tree.
|
||||
/// By definition, this is UB. Therefore, implementing use_context should be done with upmost care to invalidate and
|
||||
/// prevent any code where &T is still being held after &mut T has been taken and T has been mutated.
|
||||
///
|
||||
/// While mutating &mut T while &T is captured by listeners, we can do any of:
|
||||
/// 1) Prevent those listeners from being called and avoid "producing" UB values
|
||||
/// 2) Delete instances of closures where &T is captured before &mut T is taken
|
||||
/// 3) Make clones of T to preserve the original &T.
|
||||
/// 4) Disable any &T remotely (like RwLock, RefCell, etc)
|
||||
///
|
||||
/// To guarantee safe usage of state management solutions, we provide Dioxus-Reducer and Dioxus-Dataflow built on the
|
||||
/// SafeContext API. This should provide as an example of how to implement context safely for 3rd party state management.
|
||||
///
|
||||
/// It's important to recognize that while safety is a top concern for Dioxus, ergonomics do take prescendence.
|
||||
/// Contrasting with the JS ecosystem, Rust is faster, but actually "less safe". JS is, by default, a "safe" language.
|
||||
/// However, it does not protect you against data races: the primary concern for 3rd party implementers of Context.
|
||||
///
|
||||
/// We guarantee that any &T will remain consistent throughout the life of the Virtual Dom and that
|
||||
/// &T is owned by components owned by the VirtualDom. Therefore, it is impossible for &T to:
|
||||
/// - be dangling or unaligned
|
||||
/// - produce an invalid value
|
||||
/// - produce uninitialized memory
|
||||
///
|
||||
/// The only UB that is left to the implementer to prevent are Data Races.
|
||||
///
|
||||
/// Here's a strategy that is UB:
|
||||
/// 1. &T is handed out via use_context
|
||||
/// 2. an event is reduced against the state
|
||||
/// 3. An &mut T is taken
|
||||
/// 4. &mut T is mutated.
|
||||
///
|
||||
/// Now, any closures that caputed &T are subject to a data race where they might have skipped checks and UB
|
||||
/// *will* affect the program.
|
||||
///
|
||||
/// Here's a strategy that's not UB (implemented by SafeContext):
|
||||
/// 1. ContextGuard<T> is handed out via use_context.
|
||||
/// 2. An event is reduced against the state.
|
||||
/// 3. The state is cloned.
|
||||
/// 4. All subfield selectors are evaluated and then diffed with the original.
|
||||
/// 5. Fields that have changed have their ContextGuard poisoned, revoking their ability to take &T.a.
|
||||
/// 6. The affected fields of Context are mutated.
|
||||
/// 7. Scopes with poisoned guards are regenerated so they can take &T.a again, calling their lifecycle.
|
||||
///
|
||||
/// In essence, we've built a "partial borrowing" mechanism for Context objects.
|
||||
///
|
||||
/// =================
|
||||
/// nb
|
||||
/// =================
|
||||
/// If you want to build a state management API directly and deal with all the unsafe and UB, we provide
|
||||
/// `use_context_unchecked` with all the stability with *no* guarantess of Data Race protection. You're on
|
||||
/// your own to not affect user applications.
|
||||
///
|
||||
/// - Dioxus reducer is built on the safe API and provides a useful but slightly limited API.
|
||||
/// - Dioxus Dataflow is built on the unsafe API and provides an even snazzier API than Dioxus Reducer.
|
||||
fn _blah() {}
|
|
@ -41,11 +41,9 @@ use std::{
|
|||
cell::{RefCell, RefMut},
|
||||
cmp::Ordering,
|
||||
collections::VecDeque,
|
||||
rc::{Rc, Weak},
|
||||
sync::atomic::AtomicU32,
|
||||
// rc::{Rc, Weak},
|
||||
sync::{Arc, Weak},
|
||||
};
|
||||
type Rc<T> = Arc<T>;
|
||||
|
||||
/// The DiffState is a cursor internal to the VirtualDOM's diffing algorithm that allows persistence of state while
|
||||
/// diffing trees of components. This means we can "re-enter" a subtree of a component by queuing a "NeedToDiff" event.
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
//! </div>
|
||||
//! Dioxus: a concurrent, functional, reactive virtual dom for any renderer in Rust.
|
||||
//!
|
||||
//! This crate aims to maintain a uniform hook-based, renderer-agnostic UI framework for cross-platform development.
|
||||
//! This crate aims to maintain a hook-based, renderer-agnostic framework for cross-platform UI development.
|
||||
//!
|
||||
//! ## Components
|
||||
//! The base unit of Dioxus is the `component`. Components can be easily created from just a function - no traits required:
|
||||
|
@ -16,8 +16,8 @@
|
|||
//! #[derive(Properties)]
|
||||
//! struct Props { name: String }
|
||||
//!
|
||||
//! fn Example(ctx: &mut Context<Props>) -> VNode {
|
||||
//! html! { <div> "Hello {ctx.props.name}!" </div> }
|
||||
//! fn Example(ctx: Context, props: &Props) -> DomTree {
|
||||
//! html! { <div> "Hello {props.name}!" </div> }
|
||||
//! }
|
||||
//! ```
|
||||
//! Components need to take a "Context" parameter which is generic over some properties. This defines how the component can be used
|
||||
|
@ -37,11 +37,14 @@
|
|||
//! ```
|
||||
//!
|
||||
//! If the properties struct is too noisy for you, we also provide a macro that converts variadic functions into components automatically.
|
||||
//! Many people don't like the magic of proc macros, so this is entirely optional. Under-the-hood, we simply transplant the
|
||||
//! function arguments into a struct, so there's very little actual magic happening.
|
||||
//!
|
||||
//! ```
|
||||
//! use dioxus_core::prelude::*;
|
||||
//!
|
||||
//! #[fc]
|
||||
//! static Example: FC = |ctx, name: String| {
|
||||
//! #[derive_props]
|
||||
//! static Example: FC = |ctx, name: &String| {
|
||||
//! html! { <div> "Hello {name}!" </div> }
|
||||
//! }
|
||||
//! ```
|
||||
|
@ -49,7 +52,8 @@
|
|||
//! ## Hooks
|
||||
//! Dioxus uses hooks for state management. Hooks are a form of state persisted between calls of the function component. Instead of
|
||||
//! using a single struct to store data, hooks use the "use_hook" building block which allows the persistence of data between
|
||||
//! function component renders.
|
||||
//! function component renders. Each hook stores some data in a "memory cell" and needs to be called in a consistent order.
|
||||
//! This means hooks "anything with `use_x`" may not be called conditionally.
|
||||
//!
|
||||
//! This allows functions to reuse stateful logic between components, simplify large complex components, and adopt more clear context
|
||||
//! subscription patterns to make components easier to read.
|
||||
|
@ -67,7 +71,7 @@
|
|||
|
||||
pub mod arena;
|
||||
pub mod component; // Logic for extending FC
|
||||
pub mod context; // Logic for providing hook + context functionality to user components
|
||||
|
||||
pub mod debug_renderer;
|
||||
pub mod diff;
|
||||
pub mod patch; // An "edit phase" described by transitions and edit operations // Test harness for validating that lifecycles and diffs work appropriately
|
||||
|
@ -86,7 +90,7 @@ pub mod builder {
|
|||
// types used internally that are important
|
||||
pub(crate) mod innerlude {
|
||||
pub use crate::component::*;
|
||||
pub use crate::context::*;
|
||||
|
||||
pub use crate::debug_renderer::*;
|
||||
pub use crate::diff::*;
|
||||
pub use crate::error::*;
|
||||
|
@ -99,10 +103,6 @@ pub(crate) mod innerlude {
|
|||
|
||||
pub type FC<P> = for<'scope> fn(Context<'scope>, &'scope P) -> DomTree;
|
||||
|
||||
// TODO @Jon, fix this
|
||||
// hack the VNode type until VirtualNode is fixed in the macro crate
|
||||
pub type VirtualNode<'a> = VNode<'a>;
|
||||
|
||||
// Re-export the FC macro
|
||||
pub use crate as dioxus;
|
||||
pub use crate::nodebuilder as builder;
|
||||
|
|
|
@ -5,14 +5,10 @@
|
|||
|
||||
use crate::{
|
||||
events::VirtualEvent,
|
||||
innerlude::{Context, NodeCtx, Properties, ScopeIdx, FC},
|
||||
innerlude::{Context, Properties, ScopeIdx, FC},
|
||||
};
|
||||
|
||||
use bumpalo::Bump;
|
||||
use std::fmt::Debug;
|
||||
use std::sync::Arc;
|
||||
use std::{any::Any, cell::RefCell, marker::PhantomData};
|
||||
type Rc<T> = Arc<T>;
|
||||
use std::{cell::RefCell, fmt::Debug, marker::PhantomData, rc::Rc};
|
||||
|
||||
/// A domtree represents the result of "Viewing" the context
|
||||
/// It's a placeholder over vnodes, to make working with lifetimes easier
|
||||
|
@ -21,25 +17,16 @@ pub struct DomTree {
|
|||
pub(crate) root: VNode<'static>,
|
||||
}
|
||||
|
||||
// ==============================
|
||||
// VNODES
|
||||
// ==============================
|
||||
|
||||
/// Tools for the base unit of the virtual dom - the VNode
|
||||
/// VNodes are intended to be quickly-allocated, lightweight enum values.
|
||||
///
|
||||
/// Components will be generating a lot of these very quickly, so we want to
|
||||
/// limit the amount of heap allocations / overly large enum sizes.
|
||||
#[derive(Debug)]
|
||||
pub enum VNode<'src> {
|
||||
/// An element node (node type `ELEMENT_NODE`).
|
||||
Element(&'src VElement<'src>),
|
||||
|
||||
/// A text node (node type `TEXT_NODE`).
|
||||
///
|
||||
/// Note: This wraps a `VText` instead of a plain `String` in
|
||||
/// order to enable custom methods like `create_text_node()` on the
|
||||
/// wrapped type.
|
||||
Text(VText<'src>),
|
||||
|
||||
/// A "suspended component"
|
||||
|
@ -101,7 +88,6 @@ impl<'a> VNode<'a> {
|
|||
// ========================================================
|
||||
// VElement (div, h1, etc), attrs, keys, listener handle
|
||||
// ========================================================
|
||||
#[derive(Debug)]
|
||||
pub struct VElement<'a> {
|
||||
/// Elements have a tag name, zero or more attributes, and zero or more
|
||||
pub key: NodeKey<'a>,
|
||||
|
@ -112,20 +98,6 @@ pub struct VElement<'a> {
|
|||
pub namespace: Option<&'a str>,
|
||||
}
|
||||
|
||||
impl<'a> VElement<'a> {
|
||||
// The tag of a component MUST be known at compile time
|
||||
pub fn new(_tag: &'a str) -> Self {
|
||||
todo!()
|
||||
// VElement {
|
||||
// tag,
|
||||
// attrs: HashMap::new(),
|
||||
// events: HashMap::new(),
|
||||
// // events: Events(HashMap::new()),
|
||||
// children: vec![],
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
/// An attribute on a DOM node, such as `id="my-thing"` or
|
||||
/// `href="https://example.com"`.
|
||||
#[derive(Clone, Debug)]
|
||||
|
@ -174,18 +146,9 @@ pub struct Listener<'bump> {
|
|||
pub scope: ScopeIdx,
|
||||
pub id: usize,
|
||||
|
||||
// pub(crate) _i: &'bump str,
|
||||
// #[serde(skip_serializing, skip_deserializing, default="")]
|
||||
// /// The callback to invoke when the event happens.
|
||||
/// The callback to invoke when the event happens.
|
||||
pub(crate) callback: &'bump (dyn Fn(VirtualEvent)),
|
||||
}
|
||||
impl Debug for Listener<'_> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("Listener")
|
||||
.field("event", &self.event)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
/// The key for keyed children.
|
||||
///
|
||||
|
@ -263,12 +226,6 @@ pub struct VComponent<'src> {
|
|||
_p: PhantomData<&'src ()>,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for VComponent<'_> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> VComponent<'a> {
|
||||
// use the type parameter on props creation and move it into a portable context
|
||||
// this lets us keep scope generic *and* downcast its props when we need to:
|
||||
|
|
|
@ -29,12 +29,9 @@ use std::{
|
|||
fmt::Debug,
|
||||
future::Future,
|
||||
pin::Pin,
|
||||
// rc::{Rc, Weak},
|
||||
sync::{Arc, Weak},
|
||||
rc::{Rc, Weak},
|
||||
};
|
||||
|
||||
type Rc<T> = Arc<T>;
|
||||
|
||||
/// 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.
|
||||
pub struct VirtualDom {
|
||||
|
@ -1100,15 +1097,16 @@ mod tests {
|
|||
|
||||
// ensure the virtualdom is send + sync
|
||||
// needed for use in async/await contexts
|
||||
#[test]
|
||||
fn is_send_sync() {
|
||||
fn check_send<T: Send>(a: T) -> T {
|
||||
fn check_send<T: Send>(_a: T) -> T {
|
||||
todo!()
|
||||
}
|
||||
fn check_sync<T: Sync>(a: T) -> T {
|
||||
fn check_sync<T: Sync>(_a: T) -> T {
|
||||
todo!()
|
||||
}
|
||||
|
||||
let _ = check_send(VirtualDom::new(|ctx, props| ctx.render(rsx! { div {}})));
|
||||
let _ = check_sync(VirtualDom::new(|ctx, props| ctx.render(rsx! { div {}})));
|
||||
let _ = check_send(VirtualDom::new(|ctx, _props| ctx.render(rsx! { div {}})));
|
||||
let _ = check_sync(VirtualDom::new(|ctx, _props| ctx.render(rsx! { div {}})));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,7 +29,12 @@ static Example: FC<ExampleProps> = |ctx, props| {
|
|||
|
||||
ctx.render(rsx! {
|
||||
div {
|
||||
class: "py-12 px-4 text-center w-full max-w-2xl mx-auto"
|
||||
class: "py-12 px-4 text-center w-full max-w-2xl mx-auto",
|
||||
// classes: [Some("asd")]
|
||||
// style: {
|
||||
// a: "asd"
|
||||
// b: "ad"
|
||||
// }
|
||||
span {
|
||||
class: "text-sm font-semibold"
|
||||
"Dioxus Example: Jack and Jill"
|
||||
|
|
Loading…
Reference in New Issue