docs: document the entire core crate
This commit is contained in:
1 parent
55bb62bbc8
commit
62df5f7097
11 files changed
+253
-74
No files matched your search
@@ -257,11 +257,10 @@ impl<'b> DiffState<'b> {
|
|||||||
vcomponent.scope.set(Some(new_idx));
|
vcomponent.scope.set(Some(new_idx));
|
||||||
|
|
||||||
log::trace!(
|
log::trace!(
|
||||||
"created component \"{}\", id: {:?} parent {:?} orig: {:?}",
|
"created component \"{}\", id: {:?} parent {:?}",
|
||||||
vcomponent.fn_name,
|
vcomponent.fn_name,
|
||||||
new_idx,
|
new_idx,
|
||||||
parent_idx,
|
parent_idx,
|
||||||
vcomponent.originator
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// if vcomponent.can_memoize {
|
// if vcomponent.can_memoize {
|
||||||
@@ -957,13 +956,6 @@ impl<'b> DiffState<'b> {
|
|||||||
let props = scope.props.take().unwrap();
|
let props = scope.props.take().unwrap();
|
||||||
c.props.borrow_mut().replace(props);
|
c.props.borrow_mut().replace(props);
|
||||||
self.scopes.try_remove(scope_id).unwrap();
|
self.scopes.try_remove(scope_id).unwrap();
|
||||||
|
|
||||||
// // we can only remove components if they are actively being diffed
|
|
||||||
// if self.scope_stack.contains(&c.originator) {
|
|
||||||
// log::trace!("Removing component {:?}", old);
|
|
||||||
|
|
||||||
// self.scopes.try_remove(scope_id).unwrap();
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
self.leave_scope();
|
self.leave_scope();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -119,24 +119,39 @@ pub enum EventPriority {
|
|||||||
Low = 0,
|
Low = 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The internal Dioxus type that carries any event data to the relevant handler.
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
pub struct AnyEvent {
|
pub struct AnyEvent {
|
||||||
pub(crate) bubble_state: Rc<BubbleState>,
|
pub(crate) bubble_state: Rc<BubbleState>,
|
||||||
pub(crate) data: Arc<dyn Any + Send + Sync>,
|
pub(crate) data: Arc<dyn Any + Send + Sync>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AnyEvent {
|
impl AnyEvent {
|
||||||
|
/// Convert this AnyEvent into a specific UiEvent with EventData.
|
||||||
|
///
|
||||||
|
/// ```rust, ignore
|
||||||
|
/// let evt: FormEvent = evvt.downcast().unwrap();
|
||||||
|
/// ```
|
||||||
pub fn downcast<T: Send + Sync + 'static>(self) -> Option<UiEvent<T>> {
|
pub fn downcast<T: Send + Sync + 'static>(self) -> Option<UiEvent<T>> {
|
||||||
let AnyEvent { data, bubble_state } = self;
|
let AnyEvent { data, bubble_state } = self;
|
||||||
|
|
||||||
if let Ok(data) = data.downcast::<T>() {
|
data.downcast::<T>().ok().map(|data| UiEvent { bubble_state, data })
|
||||||
Some(UiEvent { bubble_state, data })
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A UiEvent is a type that wraps various EventData.
|
||||||
|
///
|
||||||
|
/// You should prefer to use the name of the event directly, rather than
|
||||||
|
/// the UiEvent<T> generic type.
|
||||||
|
///
|
||||||
|
/// For the HTML crate, this would include [`MouseEvent`], [`FormEvent`] etc.
|
||||||
pub struct UiEvent<T> {
|
pub struct UiEvent<T> {
|
||||||
|
/// The internal data of the event
|
||||||
|
/// This is wrapped in an Arc so that it can be sent across threads
|
||||||
pub data: Arc<T>,
|
pub data: Arc<T>,
|
||||||
|
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
|
|||||||
@@ -35,14 +35,32 @@ enum StackNodeStorage<'a, 'b> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'b> LazyNodes<'a, 'b> {
|
impl<'a, 'b> LazyNodes<'a, 'b> {
|
||||||
pub fn new_some<F>(_val: F) -> Self
|
/// Create a new LazyNodes closure, optimistically placing it onto the stack.
|
||||||
where
|
///
|
||||||
F: FnOnce(NodeFactory<'a>) -> VNode<'a> + 'b,
|
/// If the closure cannot fit into the stack allocation (16 bytes), then it
|
||||||
{
|
/// is placed on the heap. Most closures will fit into the stack, and is
|
||||||
Self::new(_val)
|
/// the most optimal way to use the creation function.
|
||||||
|
pub fn new(val: impl FnOnce(NodeFactory<'a>) -> VNode<'a> + 'b) -> Self {
|
||||||
|
// there's no way to call FnOnce without a box, so we need to store it in a slot and use static dispatch
|
||||||
|
let mut slot = Some(val);
|
||||||
|
|
||||||
|
let val = move |fac: Option<NodeFactory<'a>>| {
|
||||||
|
let inner = slot.take().unwrap();
|
||||||
|
fac.map(inner)
|
||||||
|
};
|
||||||
|
|
||||||
|
// miri does not know how to work with mucking directly into bytes
|
||||||
|
// just use a heap allocated type when miri is running
|
||||||
|
if cfg!(miri) {
|
||||||
|
Self {
|
||||||
|
inner: StackNodeStorage::Heap(Box::new(val)),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
unsafe { LazyNodes::new_inner(val) }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// force this call onto the stack
|
/// Create a new LazyNodes closure, but force it onto the heap.
|
||||||
pub fn new_boxed<F>(_val: F) -> Self
|
pub fn new_boxed<F>(_val: F) -> Self
|
||||||
where
|
where
|
||||||
F: FnOnce(NodeFactory<'a>) -> VNode<'a> + 'b,
|
F: FnOnce(NodeFactory<'a>) -> VNode<'a> + 'b,
|
||||||
@@ -60,25 +78,6 @@ impl<'a, 'b> LazyNodes<'a, 'b> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new(_val: impl FnOnce(NodeFactory<'a>) -> VNode<'a> + 'b) -> Self {
|
|
||||||
// there's no way to call FnOnce without a box, so we need to store it in a slot and use static dispatch
|
|
||||||
let mut slot = Some(_val);
|
|
||||||
|
|
||||||
let val = move |fac: Option<NodeFactory<'a>>| {
|
|
||||||
let inner = slot.take().unwrap();
|
|
||||||
fac.map(inner)
|
|
||||||
};
|
|
||||||
|
|
||||||
// miri does not know how to work with mucking directly into bytes
|
|
||||||
if cfg!(miri) {
|
|
||||||
Self {
|
|
||||||
inner: StackNodeStorage::Heap(Box::new(val)),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
unsafe { LazyNodes::new_inner(val) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn new_inner<F>(val: F) -> Self
|
unsafe fn new_inner<F>(val: F) -> Self
|
||||||
where
|
where
|
||||||
F: FnMut(Option<NodeFactory<'a>>) -> Option<VNode<'a>> + 'b,
|
F: FnMut(Option<NodeFactory<'a>>) -> Option<VNode<'a>> + 'b,
|
||||||
@@ -153,6 +152,15 @@ impl<'a, 'b> LazyNodes<'a, 'b> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Call the closure with the given factory to produce real VNodes.
|
||||||
|
///
|
||||||
|
/// ```rust, ignore
|
||||||
|
/// let f = LazyNodes::new(move |f| f.element("div", [], [], [] None));
|
||||||
|
///
|
||||||
|
/// let fac = NodeFactory::new(&cx);
|
||||||
|
///
|
||||||
|
/// let node = f.call(cac);
|
||||||
|
/// ```
|
||||||
pub fn call(self, f: NodeFactory<'a>) -> VNode<'a> {
|
pub fn call(self, f: NodeFactory<'a>) -> VNode<'a> {
|
||||||
match self.inner {
|
match self.inner {
|
||||||
StackNodeStorage::Heap(mut lazy) => lazy(Some(f)).unwrap(),
|
StackNodeStorage::Heap(mut lazy) => lazy(Some(f)).unwrap(),
|
||||||
@@ -245,9 +253,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn it_works() {
|
fn it_works() {
|
||||||
fn app(cx: Scope<()>) -> Element {
|
fn app(cx: Scope<()>) -> Element {
|
||||||
cx.render(LazyNodes::new_some(|f| {
|
cx.render(LazyNodes::new(|f| f.text(format_args!("hello world!"))))
|
||||||
f.text(format_args!("hello world!"))
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut dom = VirtualDom::new(app);
|
let mut dom = VirtualDom::new(app);
|
||||||
@@ -280,7 +286,7 @@ mod tests {
|
|||||||
.map(|i| {
|
.map(|i| {
|
||||||
let val = cx.props.inner.clone();
|
let val = cx.props.inner.clone();
|
||||||
|
|
||||||
LazyNodes::new_some(move |f| {
|
LazyNodes::new(move |f| {
|
||||||
log::debug!("hell closure");
|
log::debug!("hell closure");
|
||||||
let inner = DropInner { id: i };
|
let inner = DropInner { id: i };
|
||||||
f.text(format_args!("hello world {:?}, {:?}", inner.id, val))
|
f.text(format_args!("hello world {:?}, {:?}", inner.id, val))
|
||||||
@@ -288,7 +294,7 @@ mod tests {
|
|||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
LazyNodes::new_some(|f| {
|
LazyNodes::new(|f| {
|
||||||
log::debug!("main closure");
|
log::debug!("main closure");
|
||||||
f.fragment_from_iter(it)
|
f.fragment_from_iter(it)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#![allow(non_snake_case)]
|
#![allow(non_snake_case)]
|
||||||
#![doc = include_str!("../README.md")]
|
#![doc = include_str!("../README.md")]
|
||||||
|
#![deny(missing_docs)]
|
||||||
|
|
||||||
pub(crate) mod diff;
|
pub(crate) mod diff;
|
||||||
pub(crate) mod events;
|
pub(crate) mod events;
|
||||||
@@ -76,6 +77,9 @@ pub use crate::innerlude::{
|
|||||||
VElement, VFragment, VNode, VPlaceholder, VText, VirtualDom,
|
VElement, VFragment, VNode, VPlaceholder, VText, VirtualDom,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// The purpose of this module is to alleviate imports of many common types
|
||||||
|
///
|
||||||
|
/// This includes types like [`Scope`], [`Element`], and [`Component`].
|
||||||
pub mod prelude {
|
pub mod prelude {
|
||||||
pub use crate::innerlude::{
|
pub use crate::innerlude::{
|
||||||
fc_to_builder, Attributes, Component, DioxusElement, Element, EventHandler, Fragment,
|
fc_to_builder, Attributes, Component, DioxusElement, Element, EventHandler, Fragment,
|
||||||
|
|||||||
@@ -16,8 +16,13 @@ use std::{any::Any, fmt::Debug};
|
|||||||
///
|
///
|
||||||
/// Mutations are the only link between the RealDOM and the VirtualDOM.
|
/// Mutations are the only link between the RealDOM and the VirtualDOM.
|
||||||
pub struct Mutations<'a> {
|
pub struct Mutations<'a> {
|
||||||
|
/// The list of edits that need to be applied for the RealDOM to match the VirtualDOM.
|
||||||
pub edits: Vec<DomEdit<'a>>,
|
pub edits: Vec<DomEdit<'a>>,
|
||||||
|
|
||||||
|
/// The list of Scopes that were diffed, created, and removed during the Diff process.
|
||||||
pub dirty_scopes: FxHashSet<ScopeId>,
|
pub dirty_scopes: FxHashSet<ScopeId>,
|
||||||
|
|
||||||
|
/// The list of nodes to connect to the RealDOM.
|
||||||
pub refs: Vec<NodeRefMutation<'a>>,
|
pub refs: Vec<NodeRefMutation<'a>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,73 +44,142 @@ impl Debug for Mutations<'_> {
|
|||||||
serde(tag = "type")
|
serde(tag = "type")
|
||||||
)]
|
)]
|
||||||
pub enum DomEdit<'bump> {
|
pub enum DomEdit<'bump> {
|
||||||
|
/// Push the given root node onto our stack.
|
||||||
PushRoot {
|
PushRoot {
|
||||||
|
/// The ID of the root node to push.
|
||||||
root: u64,
|
root: u64,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/// Pop the topmost node from our stack and append them to the node
|
||||||
|
/// at the top of the stack.
|
||||||
AppendChildren {
|
AppendChildren {
|
||||||
|
/// How many nodes should be popped from the stack.
|
||||||
|
/// The node remaining on the stack will be the target for the append.
|
||||||
many: u32,
|
many: u32,
|
||||||
},
|
},
|
||||||
|
|
||||||
// // save a possibly-fragment node as a template
|
/// Replace a given (single) node with a handful of nodes currently on the stack.
|
||||||
// SaveAsTemplate {
|
|
||||||
// many: u32,
|
|
||||||
// },
|
|
||||||
|
|
||||||
// "Root" refers to the item directly
|
|
||||||
// it's a waste of an instruction to push the root directly
|
|
||||||
ReplaceWith {
|
ReplaceWith {
|
||||||
|
/// The ID of the node to be replaced.
|
||||||
root: u64,
|
root: u64,
|
||||||
|
|
||||||
|
/// How many nodes should be popped from the stack to replace the target node.
|
||||||
m: u32,
|
m: u32,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/// Insert a number of nodes after a given node.
|
||||||
InsertAfter {
|
InsertAfter {
|
||||||
|
/// The ID of the node to insert after.
|
||||||
root: u64,
|
root: u64,
|
||||||
|
|
||||||
|
/// How many nodes should be popped from the stack to insert after the target node.
|
||||||
n: u32,
|
n: u32,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/// Insert a number of nodes before a given node.
|
||||||
InsertBefore {
|
InsertBefore {
|
||||||
|
/// The ID of the node to insert before.
|
||||||
root: u64,
|
root: u64,
|
||||||
|
|
||||||
|
/// How many nodes should be popped from the stack to insert before the target node.
|
||||||
n: u32,
|
n: u32,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/// Remove a particular node from the DOM
|
||||||
Remove {
|
Remove {
|
||||||
|
/// The ID of the node to remove.
|
||||||
root: u64,
|
root: u64,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/// Create a new purely-text node
|
||||||
CreateTextNode {
|
CreateTextNode {
|
||||||
|
/// The ID the new node should have.
|
||||||
|
root: u64,
|
||||||
|
|
||||||
|
/// The textcontent of the node
|
||||||
text: &'bump str,
|
text: &'bump str,
|
||||||
root: u64,
|
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/// Create a new purely-element node
|
||||||
CreateElement {
|
CreateElement {
|
||||||
tag: &'bump str,
|
/// The ID the new node should have.
|
||||||
root: u64,
|
root: u64,
|
||||||
|
|
||||||
|
/// The tagname of the node
|
||||||
|
tag: &'bump str,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/// Create a new purely-comment node with a given namespace
|
||||||
CreateElementNs {
|
CreateElementNs {
|
||||||
tag: &'bump str,
|
/// The ID the new node should have.
|
||||||
root: u64,
|
root: u64,
|
||||||
|
|
||||||
|
/// The namespace of the node
|
||||||
|
tag: &'bump str,
|
||||||
|
|
||||||
|
/// The namespace of the node (like `SVG`)
|
||||||
ns: &'static str,
|
ns: &'static str,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/// Create a new placeholder node.
|
||||||
|
/// In most implementations, this will either be a hidden div or a comment node.
|
||||||
CreatePlaceholder {
|
CreatePlaceholder {
|
||||||
|
/// The ID the new node should have.
|
||||||
root: u64,
|
root: u64,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/// Create a new Event Listener.
|
||||||
NewEventListener {
|
NewEventListener {
|
||||||
|
/// The name of the event to listen for.
|
||||||
event_name: &'static str,
|
event_name: &'static str,
|
||||||
|
|
||||||
|
/// The ID of the node to attach the listener to.
|
||||||
scope: ScopeId,
|
scope: ScopeId,
|
||||||
|
|
||||||
|
/// The ID of the node to attach the listener to.
|
||||||
root: u64,
|
root: u64,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/// Remove an existing Event Listener.
|
||||||
RemoveEventListener {
|
RemoveEventListener {
|
||||||
|
/// The ID of the node to remove.
|
||||||
root: u64,
|
root: u64,
|
||||||
|
|
||||||
|
/// The name of the event to remove.
|
||||||
event: &'static str,
|
event: &'static str,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/// Set the textcontent of a node.
|
||||||
SetText {
|
SetText {
|
||||||
|
/// The ID of the node to set the textcontent of.
|
||||||
root: u64,
|
root: u64,
|
||||||
|
|
||||||
|
/// The textcontent of the node
|
||||||
text: &'bump str,
|
text: &'bump str,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/// Set the value of a node's attribute.
|
||||||
SetAttribute {
|
SetAttribute {
|
||||||
|
/// The ID of the node to set the attribute of.
|
||||||
root: u64,
|
root: u64,
|
||||||
|
|
||||||
|
/// The name of the attribute to set.
|
||||||
field: &'static str,
|
field: &'static str,
|
||||||
|
|
||||||
|
/// The value of the attribute.
|
||||||
value: &'bump str,
|
value: &'bump str,
|
||||||
|
|
||||||
|
/// The (optional) namespace of the attribute.
|
||||||
|
/// For instance, "style" is in the "style" namespace.
|
||||||
ns: Option<&'bump str>,
|
ns: Option<&'bump str>,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/// Remove an attribute from a node.
|
||||||
RemoveAttribute {
|
RemoveAttribute {
|
||||||
|
/// The ID of the node to remove.
|
||||||
root: u64,
|
root: u64,
|
||||||
|
|
||||||
|
/// The name of the attribute to remove.
|
||||||
name: &'static str,
|
name: &'static str,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
+81
-14
@@ -148,7 +148,7 @@ impl<'src> VNode<'src> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create an "owned" version of the vnode.
|
// Create an "owned" version of the vnode.
|
||||||
pub fn decouple(&self) -> VNode<'src> {
|
pub(crate) fn decouple(&self) -> VNode<'src> {
|
||||||
match *self {
|
match *self {
|
||||||
VNode::Text(t) => VNode::Text(t),
|
VNode::Text(t) => VNode::Text(t),
|
||||||
VNode::Element(e) => VNode::Element(e),
|
VNode::Element(e) => VNode::Element(e),
|
||||||
@@ -181,7 +181,6 @@ impl Debug for VNode<'_> {
|
|||||||
.field("fnptr", &comp.user_fc)
|
.field("fnptr", &comp.user_fc)
|
||||||
.field("key", &comp.key)
|
.field("key", &comp.key)
|
||||||
.field("scope", &comp.scope)
|
.field("scope", &comp.scope)
|
||||||
.field("originator", &comp.originator)
|
|
||||||
.finish(),
|
.finish(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -200,6 +199,7 @@ impl std::fmt::Display for ElementId {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ElementId {
|
impl ElementId {
|
||||||
|
/// Convertt the ElementId to a `u64`.
|
||||||
pub fn as_u64(self) -> u64 {
|
pub fn as_u64(self) -> u64 {
|
||||||
self.0 as u64
|
self.0 as u64
|
||||||
}
|
}
|
||||||
@@ -211,18 +211,26 @@ fn empty_cell() -> Cell<Option<ElementId>> {
|
|||||||
|
|
||||||
/// A placeholder node only generated when Fragments don't have any children.
|
/// A placeholder node only generated when Fragments don't have any children.
|
||||||
pub struct VPlaceholder {
|
pub struct VPlaceholder {
|
||||||
|
/// The [`ElementId`] of the placeholder.
|
||||||
pub id: Cell<Option<ElementId>>,
|
pub id: Cell<Option<ElementId>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A bump-allocated string slice and metadata.
|
/// A bump-allocated string slice and metadata.
|
||||||
pub struct VText<'src> {
|
pub struct VText<'src> {
|
||||||
pub text: &'src str,
|
/// The [`ElementId`] of the VText.
|
||||||
pub id: Cell<Option<ElementId>>,
|
pub id: Cell<Option<ElementId>>,
|
||||||
|
|
||||||
|
/// The text of the VText.
|
||||||
|
pub text: &'src str,
|
||||||
|
|
||||||
|
/// An indiciation if this VText can be ignored during diffing
|
||||||
|
/// Is usually only when there are no strings to be formatted (so the text is &'static str)
|
||||||
pub is_static: bool,
|
pub is_static: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A list of VNodes with no single root.
|
/// A list of VNodes with no single root.
|
||||||
pub struct VFragment<'src> {
|
pub struct VFragment<'src> {
|
||||||
|
/// The key of the fragment to be used during keyed diffing.
|
||||||
pub key: Option<&'src str>,
|
pub key: Option<&'src str>,
|
||||||
|
|
||||||
/// Fragments can never have zero children. Enforced by NodeFactory.
|
/// Fragments can never have zero children. Enforced by NodeFactory.
|
||||||
@@ -233,13 +241,34 @@ pub struct VFragment<'src> {
|
|||||||
|
|
||||||
/// An element like a "div" with children, listeners, and attributes.
|
/// An element like a "div" with children, listeners, and attributes.
|
||||||
pub struct VElement<'a> {
|
pub struct VElement<'a> {
|
||||||
pub tag: &'static str,
|
/// The [`ElementId`] of the VText.
|
||||||
pub namespace: Option<&'static str>,
|
|
||||||
pub key: Option<&'a str>,
|
|
||||||
pub id: Cell<Option<ElementId>>,
|
pub id: Cell<Option<ElementId>>,
|
||||||
|
|
||||||
|
/// The key of the element to be used during keyed diffing.
|
||||||
|
pub key: Option<&'a str>,
|
||||||
|
|
||||||
|
/// The tag name of the element.
|
||||||
|
///
|
||||||
|
/// IE "div"
|
||||||
|
pub tag: &'static str,
|
||||||
|
|
||||||
|
/// The namespace of the VElement
|
||||||
|
///
|
||||||
|
/// IE "svg"
|
||||||
|
pub namespace: Option<&'static str>,
|
||||||
|
|
||||||
|
/// The parent of the Element (if any).
|
||||||
|
///
|
||||||
|
/// Used when bubbling events
|
||||||
pub parent: Cell<Option<ElementId>>,
|
pub parent: Cell<Option<ElementId>>,
|
||||||
|
|
||||||
|
/// The Listeners of the VElement.
|
||||||
pub listeners: &'a [Listener<'a>],
|
pub listeners: &'a [Listener<'a>],
|
||||||
|
|
||||||
|
/// The attributes of the VElement.
|
||||||
pub attributes: &'a [Attribute<'a>],
|
pub attributes: &'a [Attribute<'a>],
|
||||||
|
|
||||||
|
/// The children of the VElement.
|
||||||
pub children: &'a [VNode<'a>],
|
pub children: &'a [VNode<'a>],
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -275,12 +304,19 @@ impl Debug for VElement<'_> {
|
|||||||
/// };
|
/// };
|
||||||
/// ```
|
/// ```
|
||||||
pub trait DioxusElement {
|
pub trait DioxusElement {
|
||||||
|
/// The tag name of the element.
|
||||||
const TAG_NAME: &'static str;
|
const TAG_NAME: &'static str;
|
||||||
|
|
||||||
|
/// The namespace of the element.
|
||||||
const NAME_SPACE: Option<&'static str>;
|
const NAME_SPACE: Option<&'static str>;
|
||||||
|
|
||||||
|
/// The tag name of the element.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn tag_name(&self) -> &'static str {
|
fn tag_name(&self) -> &'static str {
|
||||||
Self::TAG_NAME
|
Self::TAG_NAME
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The namespace of the element.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn namespace(&self) -> Option<&'static str> {
|
fn namespace(&self) -> Option<&'static str> {
|
||||||
Self::NAME_SPACE
|
Self::NAME_SPACE
|
||||||
@@ -291,16 +327,25 @@ pub trait DioxusElement {
|
|||||||
/// `href="https://example.com"`.
|
/// `href="https://example.com"`.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Attribute<'a> {
|
pub struct Attribute<'a> {
|
||||||
|
/// The name of the attribute.
|
||||||
pub name: &'static str,
|
pub name: &'static str,
|
||||||
|
|
||||||
|
/// The value of the attribute.
|
||||||
pub value: &'a str,
|
pub value: &'a str,
|
||||||
|
|
||||||
|
/// An indication if this attribute can be ignored during diffing
|
||||||
|
///
|
||||||
|
/// Usually only when there are no strings to be formatted (so the value is &'static str)
|
||||||
pub is_static: bool,
|
pub is_static: bool,
|
||||||
|
|
||||||
|
/// An indication of we should always try and set the attribute.
|
||||||
|
/// Used in controlled components to ensure changes are propagated.
|
||||||
pub is_volatile: bool,
|
pub is_volatile: bool,
|
||||||
|
|
||||||
// Doesn't exist in the html spec.
|
/// The namespace of the attribute.
|
||||||
// Used in Dioxus to denote "style" tags.
|
///
|
||||||
|
/// Doesn't exist in the html spec.
|
||||||
|
/// Used in Dioxus to denote "style" tags and other attribute groups.
|
||||||
pub namespace: Option<&'static str>,
|
pub namespace: Option<&'static str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -353,6 +398,8 @@ type ExternalListenerCallback<'bump, T> = BumpBox<'bump, dyn FnMut(T) + 'bump>;
|
|||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
pub struct EventHandler<'bump, T = ()> {
|
pub struct EventHandler<'bump, T = ()> {
|
||||||
|
/// The (optional) callback that the user specified
|
||||||
|
/// Uses a `RefCell` to allow for interior mutability, and FnMut closures.
|
||||||
pub callback: RefCell<Option<ExternalListenerCallback<'bump, T>>>,
|
pub callback: RefCell<Option<ExternalListenerCallback<'bump, T>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -381,12 +428,23 @@ impl<T> EventHandler<'_, T> {
|
|||||||
/// Virtual Components for custom user-defined components
|
/// Virtual Components for custom user-defined components
|
||||||
/// Only supports the functional syntax
|
/// Only supports the functional syntax
|
||||||
pub struct VComponent<'src> {
|
pub struct VComponent<'src> {
|
||||||
|
/// The key of the component to be used during keyed diffing.
|
||||||
pub key: Option<&'src str>,
|
pub key: Option<&'src str>,
|
||||||
pub originator: ScopeId,
|
|
||||||
|
/// The ID of the component.
|
||||||
|
/// Will not be assigned until after the component has been initialized.
|
||||||
pub scope: Cell<Option<ScopeId>>,
|
pub scope: Cell<Option<ScopeId>>,
|
||||||
|
|
||||||
|
/// An indication if the component is static (can be memozied)
|
||||||
pub can_memoize: bool,
|
pub can_memoize: bool,
|
||||||
|
|
||||||
|
/// The function pointer to the component's render function.
|
||||||
pub user_fc: ComponentPtr,
|
pub user_fc: ComponentPtr,
|
||||||
|
|
||||||
|
/// The actual name of the component.
|
||||||
pub fn_name: &'static str,
|
pub fn_name: &'static str,
|
||||||
|
|
||||||
|
/// The props of the component.
|
||||||
pub props: RefCell<Option<Box<dyn AnyProps + 'src>>>,
|
pub props: RefCell<Option<Box<dyn AnyProps + 'src>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -434,6 +492,7 @@ pub struct NodeFactory<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> NodeFactory<'a> {
|
impl<'a> NodeFactory<'a> {
|
||||||
|
/// Create a new [`NodeFactory`] from a [`Scope`] or [`ScopeState`]
|
||||||
pub fn new(scope: &'a ScopeState) -> NodeFactory<'a> {
|
pub fn new(scope: &'a ScopeState) -> NodeFactory<'a> {
|
||||||
NodeFactory {
|
NodeFactory {
|
||||||
scope,
|
scope,
|
||||||
@@ -441,6 +500,7 @@ impl<'a> NodeFactory<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the custom alloactor for this component
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn bump(&self) -> &'a bumpalo::Bump {
|
pub fn bump(&self) -> &'a bumpalo::Bump {
|
||||||
self.bump
|
self.bump
|
||||||
@@ -482,6 +542,7 @@ impl<'a> NodeFactory<'a> {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a new [`VNode::VElement`]
|
||||||
pub fn element(
|
pub fn element(
|
||||||
&self,
|
&self,
|
||||||
el: impl DioxusElement,
|
el: impl DioxusElement,
|
||||||
@@ -500,6 +561,9 @@ impl<'a> NodeFactory<'a> {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a new [`VNode::VElement`] without the trait bound
|
||||||
|
///
|
||||||
|
/// IE pass in "div" instead of `div`
|
||||||
pub fn raw_element(
|
pub fn raw_element(
|
||||||
&self,
|
&self,
|
||||||
tag_name: &'static str,
|
tag_name: &'static str,
|
||||||
@@ -529,6 +593,7 @@ impl<'a> NodeFactory<'a> {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a new [`Attribute`]
|
||||||
pub fn attr(
|
pub fn attr(
|
||||||
&self,
|
&self,
|
||||||
name: &'static str,
|
name: &'static str,
|
||||||
@@ -546,6 +611,7 @@ impl<'a> NodeFactory<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a new [`VNode::VComponent`]
|
||||||
pub fn component<P>(
|
pub fn component<P>(
|
||||||
&self,
|
&self,
|
||||||
component: fn(Scope<'a, P>) -> Element,
|
component: fn(Scope<'a, P>) -> Element,
|
||||||
@@ -561,11 +627,8 @@ impl<'a> NodeFactory<'a> {
|
|||||||
scope: Default::default(),
|
scope: Default::default(),
|
||||||
can_memoize: P::IS_STATIC,
|
can_memoize: P::IS_STATIC,
|
||||||
user_fc: component as ComponentPtr,
|
user_fc: component as ComponentPtr,
|
||||||
originator: self.scope.scope_id(),
|
|
||||||
fn_name,
|
fn_name,
|
||||||
props: RefCell::new(Some(Box::new(VComponentProps {
|
props: RefCell::new(Some(Box::new(VComponentProps {
|
||||||
// local_props: RefCell::new(Some(props)),
|
|
||||||
// heap_props: RefCell::new(None),
|
|
||||||
props,
|
props,
|
||||||
memo: P::memoize, // smuggle the memoization function across borders
|
memo: P::memoize, // smuggle the memoization function across borders
|
||||||
|
|
||||||
@@ -586,6 +649,7 @@ impl<'a> NodeFactory<'a> {
|
|||||||
VNode::Component(vcomp)
|
VNode::Component(vcomp)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a new [`Listener`]
|
||||||
pub fn listener(self, event: &'static str, callback: InternalHandler<'a>) -> Listener<'a> {
|
pub fn listener(self, event: &'static str, callback: InternalHandler<'a>) -> Listener<'a> {
|
||||||
Listener {
|
Listener {
|
||||||
event,
|
event,
|
||||||
@@ -594,6 +658,7 @@ impl<'a> NodeFactory<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a new [`VNode::VFragment`] from a root of the rsx! call
|
||||||
pub fn fragment_root<'b, 'c>(
|
pub fn fragment_root<'b, 'c>(
|
||||||
self,
|
self,
|
||||||
node_iter: impl IntoIterator<Item = impl IntoVNode<'a> + 'c> + 'b,
|
node_iter: impl IntoIterator<Item = impl IntoVNode<'a> + 'c> + 'b,
|
||||||
@@ -614,6 +679,7 @@ impl<'a> NodeFactory<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a new [`VNode::VFragment`] from any iterator
|
||||||
pub fn fragment_from_iter<'b, 'c>(
|
pub fn fragment_from_iter<'b, 'c>(
|
||||||
self,
|
self,
|
||||||
node_iter: impl IntoIterator<Item = impl IntoVNode<'a> + 'c> + 'b,
|
node_iter: impl IntoIterator<Item = impl IntoVNode<'a> + 'c> + 'b,
|
||||||
@@ -653,8 +719,7 @@ impl<'a> NodeFactory<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// this isn't quite feasible yet
|
/// Create a new [`VNode`] from any iterator of children
|
||||||
// I think we need some form of interior mutability or state on nodefactory that stores which subtree was created
|
|
||||||
pub fn create_children(
|
pub fn create_children(
|
||||||
self,
|
self,
|
||||||
node_iter: impl IntoIterator<Item = impl IntoVNode<'a>>,
|
node_iter: impl IntoIterator<Item = impl IntoVNode<'a>>,
|
||||||
@@ -679,6 +744,7 @@ impl<'a> NodeFactory<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a new [`EventHandler`] from an [`FnMut`]
|
||||||
pub fn event_handler<T>(self, f: impl FnMut(T) + 'a) -> EventHandler<'a, T> {
|
pub fn event_handler<T>(self, f: impl FnMut(T) + 'a) -> EventHandler<'a, T> {
|
||||||
let handler: &mut dyn FnMut(T) = self.bump.alloc(f);
|
let handler: &mut dyn FnMut(T) = self.bump.alloc(f);
|
||||||
let caller = unsafe { BumpBox::from_raw(handler as *mut dyn FnMut(T)) };
|
let caller = unsafe { BumpBox::from_raw(handler as *mut dyn FnMut(T)) };
|
||||||
@@ -709,6 +775,7 @@ impl Debug for NodeFactory<'_> {
|
|||||||
/// As such, all node creation must go through the factory, which is only available in the component context.
|
/// As such, all node creation must go through the factory, which is only available in the component context.
|
||||||
/// These strict requirements make it possible to manage lifetimes and state.
|
/// These strict requirements make it possible to manage lifetimes and state.
|
||||||
pub trait IntoVNode<'a> {
|
pub trait IntoVNode<'a> {
|
||||||
|
/// Convert this into a [`VNode`], using the [`NodeFactory`] as a source of allocation
|
||||||
fn into_vnode(self, cx: NodeFactory<'a>) -> VNode<'a>;
|
fn into_vnode(self, cx: NodeFactory<'a>) -> VNode<'a>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -128,8 +128,14 @@ pub fn Fragment<'a>(cx: Scope<'a, FragmentProps<'a>>) -> Element {
|
|||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
pub trait Properties: Sized {
|
pub trait Properties: Sized {
|
||||||
|
/// The type of the builder for this component.
|
||||||
|
/// Used to create "in-progress" versions of the props.
|
||||||
type Builder;
|
type Builder;
|
||||||
|
|
||||||
|
/// An indication if these props are can be memoized automatically.
|
||||||
const IS_STATIC: bool;
|
const IS_STATIC: bool;
|
||||||
|
|
||||||
|
/// Create a builder for this component.
|
||||||
fn builder() -> Self::Builder;
|
fn builder() -> Self::Builder;
|
||||||
|
|
||||||
/// Memoization can only happen if the props are valid for the 'static lifetime
|
/// Memoization can only happen if the props are valid for the 'static lifetime
|
||||||
|
|||||||
@@ -396,7 +396,10 @@ impl ScopeArena {
|
|||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
pub struct Scope<'a, P = ()> {
|
pub struct Scope<'a, P = ()> {
|
||||||
|
/// The internal ScopeState for this component
|
||||||
pub scope: &'a ScopeState,
|
pub scope: &'a ScopeState,
|
||||||
|
|
||||||
|
/// The props for this component
|
||||||
pub props: &'a P,
|
pub props: &'a P,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -747,7 +750,8 @@ impl ScopeState {
|
|||||||
self.push_future(fut);
|
self.push_future(fut);
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo: attach some state to the future to know if we should poll it
|
/// Informs the scheduler that this task is no longer needed and should be removed
|
||||||
|
/// on next poll.
|
||||||
pub fn remove_future(&self, id: TaskId) {
|
pub fn remove_future(&self, id: TaskId) {
|
||||||
self.tasks.remove_fut(id);
|
self.tasks.remove_fut(id);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
use crate::innerlude::*;
|
use crate::innerlude::*;
|
||||||
|
|
||||||
|
/// An iterator that only yields "real" [`Element`]s. IE only Elements that are
|
||||||
|
/// not [`VNode::VComponent`] or [`VNode::VFragment`], .
|
||||||
pub struct ElementIdIterator<'a> {
|
pub struct ElementIdIterator<'a> {
|
||||||
vdom: &'a VirtualDom,
|
vdom: &'a VirtualDom,
|
||||||
|
|
||||||
@@ -9,6 +11,9 @@ pub struct ElementIdIterator<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> ElementIdIterator<'a> {
|
impl<'a> ElementIdIterator<'a> {
|
||||||
|
/// Create a new iterator from the given [`VirtualDom`] and [`VNode`]
|
||||||
|
///
|
||||||
|
/// This will allow you to iterate through all the real childrne of the [`VNode`].
|
||||||
pub fn new(vdom: &'a VirtualDom, node: &'a VNode<'a>) -> Self {
|
pub fn new(vdom: &'a VirtualDom, node: &'a VNode<'a>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
vdom,
|
vdom,
|
||||||
|
|||||||
@@ -114,15 +114,18 @@ pub struct VirtualDom {
|
|||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The type of message that can be sent to the scheduler.
|
||||||
|
///
|
||||||
|
/// These messages control how the scheduler will process updates to the UI.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum SchedulerMsg {
|
pub enum SchedulerMsg {
|
||||||
// events from the host
|
/// Events from the Renderer
|
||||||
Event(UserEvent),
|
Event(UserEvent),
|
||||||
|
|
||||||
// setstate
|
/// Immediate updates from Components that mark them as dirty
|
||||||
Immediate(ScopeId),
|
Immediate(ScopeId),
|
||||||
|
|
||||||
// an async task pushed from an event handler (or just spawned)
|
/// New tasks from components that should be polled when the next poll is ready
|
||||||
NewTask(ScopeId),
|
NewTask(ScopeId),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -388,6 +391,9 @@ impl VirtualDom {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Handle an individual message for the scheduler.
|
||||||
|
///
|
||||||
|
/// This will either call an event listener or mark a component as dirty.
|
||||||
pub fn process_message(&mut self, msg: SchedulerMsg) {
|
pub fn process_message(&mut self, msg: SchedulerMsg) {
|
||||||
match msg {
|
match msg {
|
||||||
SchedulerMsg::NewTask(_id) => {
|
SchedulerMsg::NewTask(_id) => {
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ impl ToTokens for CallBody {
|
|||||||
match &self.custom_context {
|
match &self.custom_context {
|
||||||
// The `in cx` pattern allows directly rendering
|
// The `in cx` pattern allows directly rendering
|
||||||
Some(ident) => out_tokens.append_all(quote! {
|
Some(ident) => out_tokens.append_all(quote! {
|
||||||
#ident.render(LazyNodes::new_some(move |__cx: NodeFactory| -> VNode {
|
#ident.render(LazyNodes::new(move |__cx: NodeFactory| -> VNode {
|
||||||
use dioxus_elements::{GlobalAttributes, SvgAttributes};
|
use dioxus_elements::{GlobalAttributes, SvgAttributes};
|
||||||
#inner
|
#inner
|
||||||
}))
|
}))
|
||||||
@@ -87,7 +87,7 @@ impl ToTokens for CallBody {
|
|||||||
|
|
||||||
// Otherwise we just build the LazyNode wrapper
|
// Otherwise we just build the LazyNode wrapper
|
||||||
None => out_tokens.append_all(quote! {
|
None => out_tokens.append_all(quote! {
|
||||||
LazyNodes::new_some(move |__cx: NodeFactory| -> VNode {
|
LazyNodes::new(move |__cx: NodeFactory| -> VNode {
|
||||||
use dioxus_elements::{GlobalAttributes, SvgAttributes};
|
use dioxus_elements::{GlobalAttributes, SvgAttributes};
|
||||||
#inner
|
#inner
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in new issue
Block a user