1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198 |
- use dioxus_core_types::DioxusFormattable;
- use crate::events::ListenerCallback;
- use crate::innerlude::VProps;
- use crate::prelude::RenderError;
- use crate::{any_props::BoxedAnyProps, innerlude::ScopeState};
- use crate::{arena::ElementId, Element, Event};
- use crate::{
- innerlude::{ElementRef, MountId},
- properties::ComponentFunction,
- };
- use crate::{Properties, ScopeId, VirtualDom};
- use std::ops::Deref;
- use std::rc::Rc;
- use std::vec;
- use std::{
- any::{Any, TypeId},
- cell::Cell,
- fmt::{Arguments, Debug},
- };
- /// The information about the
- #[derive(Debug)]
- pub(crate) struct VNodeMount {
- /// The parent of this node
- pub parent: Option<ElementRef>,
- /// A back link to the original node
- pub node: VNode,
- /// The IDs for the roots of this template - to be used when moving the template around and removing it from
- /// the actual Dom
- pub root_ids: Box<[ElementId]>,
- /// The element in the DOM that each attribute is mounted to
- pub(crate) mounted_attributes: Box<[ElementId]>,
- /// For components: This is the ScopeId the component is mounted to
- /// For other dynamic nodes: This is element in the DOM that each dynamic node is mounted to
- pub(crate) mounted_dynamic_nodes: Box<[usize]>,
- }
- /// A reference to a template along with any context needed to hydrate it
- ///
- /// The dynamic parts of the template are stored separately from the static parts. This allows faster diffing by skipping
- /// static parts of the template.
- #[derive(Debug)]
- pub struct VNodeInner {
- /// The key given to the root of this template.
- ///
- /// In fragments, this is the key of the first child. In other cases, it is the key of the root.
- pub key: Option<String>,
- /// The static nodes and static descriptor of the template
- pub template: Template,
- /// The dynamic nodes in the template
- pub dynamic_nodes: Box<[DynamicNode]>,
- /// The dynamic attribute slots in the template
- ///
- /// This is a list of positions in the template where dynamic attributes can be inserted.
- ///
- /// The inner list *must* be in the format [static named attributes, remaining dynamically named attributes].
- ///
- /// For example:
- /// ```rust
- /// # use dioxus::prelude::*;
- /// let class = "my-class";
- /// let attrs = vec![];
- /// let color = "red";
- ///
- /// rsx! {
- /// div {
- /// class: "{class}",
- /// ..attrs,
- /// p {
- /// color: "{color}",
- /// }
- /// }
- /// };
- /// ```
- ///
- /// Would be represented as:
- /// ```text
- /// [
- /// [class, every attribute in attrs sorted by name], // Slot 0 in the template
- /// [color], // Slot 1 in the template
- /// ]
- /// ```
- pub dynamic_attrs: Box<[Box<[Attribute]>]>,
- }
- /// A reference to a template along with any context needed to hydrate it
- ///
- /// The dynamic parts of the template are stored separately from the static parts. This allows faster diffing by skipping
- /// static parts of the template.
- #[derive(Debug, Clone)]
- pub struct VNode {
- vnode: Rc<VNodeInner>,
- /// The mount information for this template
- pub(crate) mount: Cell<MountId>,
- }
- impl AsRef<VNode> for Element {
- fn as_ref(&self) -> &VNode {
- match self {
- Element::Ok(node) => node,
- Element::Err(RenderError::Aborted(err)) => &err.render,
- Element::Err(RenderError::Suspended(fut)) => &fut.placeholder,
- }
- }
- }
- impl From<&Element> for VNode {
- fn from(val: &Element) -> Self {
- AsRef::as_ref(val).clone()
- }
- }
- impl From<Element> for VNode {
- fn from(val: Element) -> Self {
- match val {
- Element::Ok(node) => node,
- Element::Err(RenderError::Aborted(err)) => err.render,
- Element::Err(RenderError::Suspended(fut)) => fut.placeholder,
- }
- }
- }
- /// A tiny helper trait to get the vnode for a Element
- pub(crate) trait AsVNode {
- /// Get the vnode for this element
- fn as_vnode(&self) -> &VNode;
- /// Create a deep clone of this VNode
- fn deep_clone(&self) -> Self;
- }
- impl AsVNode for Element {
- fn as_vnode(&self) -> &VNode {
- AsRef::as_ref(self)
- }
- fn deep_clone(&self) -> Self {
- match self {
- Ok(node) => Ok(node.deep_clone()),
- Err(RenderError::Aborted(err)) => Err(RenderError::Aborted(err.deep_clone())),
- Err(RenderError::Suspended(fut)) => Err(RenderError::Suspended(fut.deep_clone())),
- }
- }
- }
- impl Default for VNode {
- fn default() -> Self {
- Self::placeholder()
- }
- }
- impl PartialEq for VNode {
- fn eq(&self, other: &Self) -> bool {
- Rc::ptr_eq(&self.vnode, &other.vnode)
- }
- }
- impl Deref for VNode {
- type Target = VNodeInner;
- fn deref(&self) -> &Self::Target {
- &self.vnode
- }
- }
- impl VNode {
- /// Create a template with no nodes that will be skipped over during diffing
- pub fn empty() -> Element {
- Ok(Self::default())
- }
- /// Create a template with a single placeholder node
- pub fn placeholder() -> Self {
- use std::cell::OnceCell;
- // We can reuse all placeholders across the same thread to save memory
- thread_local! {
- static PLACEHOLDER_VNODE: OnceCell<Rc<VNodeInner>> = const { OnceCell::new() };
- }
- let vnode = PLACEHOLDER_VNODE.with(|cell| {
- cell.get_or_init(move || {
- Rc::new(VNodeInner {
- key: None,
- dynamic_nodes: Box::new([DynamicNode::Placeholder(Default::default())]),
- dynamic_attrs: Box::new([]),
- template: Template {
- roots: &[TemplateNode::Dynamic { id: 0 }],
- node_paths: &[&[0]],
- attr_paths: &[],
- },
- })
- })
- .clone()
- });
- Self {
- vnode,
- mount: Default::default(),
- }
- }
- /// Create a new VNode
- pub fn new(
- key: Option<String>,
- template: Template,
- dynamic_nodes: Box<[DynamicNode]>,
- dynamic_attrs: Box<[Box<[Attribute]>]>,
- ) -> Self {
- Self {
- vnode: Rc::new(VNodeInner {
- key,
- template,
- dynamic_nodes,
- dynamic_attrs,
- }),
- mount: Default::default(),
- }
- }
- /// Load a dynamic root at the given index
- ///
- /// Returns [`None`] if the root is actually a static node (Element/Text)
- pub fn dynamic_root(&self, idx: usize) -> Option<&DynamicNode> {
- self.template.roots[idx]
- .dynamic_id()
- .map(|id| &self.dynamic_nodes[id])
- }
- /// Get the mounted id for a dynamic node index
- pub fn mounted_dynamic_node(
- &self,
- dynamic_node_idx: usize,
- dom: &VirtualDom,
- ) -> Option<ElementId> {
- let mount = self.mount.get().as_usize()?;
- match &self.dynamic_nodes[dynamic_node_idx] {
- DynamicNode::Text(_) | DynamicNode::Placeholder(_) => {
- let mounts = dom.runtime.mounts.borrow();
- mounts
- .get(mount)?
- .mounted_dynamic_nodes
- .get(dynamic_node_idx)
- .map(|id| ElementId(*id))
- }
- _ => None,
- }
- }
- /// Get the mounted id for a root node index
- pub fn mounted_root(&self, root_idx: usize, dom: &VirtualDom) -> Option<ElementId> {
- let mount = self.mount.get().as_usize()?;
- let mounts = dom.runtime.mounts.borrow();
- mounts.get(mount)?.root_ids.get(root_idx).copied()
- }
- /// Get the mounted id for a dynamic attribute index
- pub fn mounted_dynamic_attribute(
- &self,
- dynamic_attribute_idx: usize,
- dom: &VirtualDom,
- ) -> Option<ElementId> {
- let mount = self.mount.get().as_usize()?;
- let mounts = dom.runtime.mounts.borrow();
- mounts
- .get(mount)?
- .mounted_attributes
- .get(dynamic_attribute_idx)
- .copied()
- }
- /// Create a deep clone of this VNode
- pub(crate) fn deep_clone(&self) -> Self {
- Self {
- vnode: Rc::new(VNodeInner {
- key: self.vnode.key.clone(),
- template: self.vnode.template,
- dynamic_nodes: self
- .vnode
- .dynamic_nodes
- .iter()
- .map(|node| match node {
- DynamicNode::Fragment(nodes) => DynamicNode::Fragment(
- nodes.iter().map(|node| node.deep_clone()).collect(),
- ),
- other => other.clone(),
- })
- .collect(),
- dynamic_attrs: self
- .vnode
- .dynamic_attrs
- .iter()
- .map(|attr| {
- attr.iter()
- .map(|attribute| attribute.deep_clone())
- .collect()
- })
- .collect(),
- }),
- mount: Default::default(),
- }
- }
- }
- type StaticStr = &'static str;
- type StaticPathArray = &'static [&'static [u8]];
- type StaticTemplateArray = &'static [TemplateNode];
- type StaticTemplateAttributeArray = &'static [TemplateAttribute];
- /// A static layout of a UI tree that describes a set of dynamic and static nodes.
- ///
- /// This is the core innovation in Dioxus. Most UIs are made of static nodes, yet participate in diffing like any
- /// dynamic node. This struct can be created at compile time. It promises that its pointer is unique, allow Dioxus to use
- /// its static description of the UI to skip immediately to the dynamic nodes during diffing.
- #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
- #[derive(Debug, Clone, Copy, Eq, PartialOrd, Ord)]
- pub struct Template {
- /// The list of template nodes that make up the template
- ///
- /// Unlike react, calls to `rsx!` can have multiple roots. This list supports that paradigm.
- #[cfg_attr(feature = "serialize", serde(deserialize_with = "deserialize_leaky"))]
- pub roots: StaticTemplateArray,
- /// The paths of each node relative to the root of the template.
- ///
- /// These will be one segment shorter than the path sent to the renderer since those paths are relative to the
- /// topmost element, not the `roots` field.
- #[cfg_attr(
- feature = "serialize",
- serde(deserialize_with = "deserialize_bytes_leaky")
- )]
- pub node_paths: StaticPathArray,
- /// The paths of each dynamic attribute relative to the root of the template
- ///
- /// These will be one segment shorter than the path sent to the renderer since those paths are relative to the
- /// topmost element, not the `roots` field.
- #[cfg_attr(
- feature = "serialize",
- serde(deserialize_with = "deserialize_bytes_leaky", bound = "")
- )]
- pub attr_paths: StaticPathArray,
- }
- // Are identical static items merged in the current build. Rust doesn't have a cfg(merge_statics) attribute
- // so we have to check this manually
- #[allow(unpredictable_function_pointer_comparisons)] // This attribute should be removed once MSRV is 1.85 or greater and the below change is made
- fn static_items_merged() -> bool {
- fn a() {}
- fn b() {}
- a as fn() == b as fn()
- // std::ptr::fn_addr_eq(a as fn(), b as fn()) <<<<---- This should replace the a as fn() === b as fn() once the MSRV is 1.85 or greater
- }
- impl std::hash::Hash for Template {
- fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
- // If identical static items are merged, we can compare templates by pointer
- if static_items_merged() {
- std::ptr::hash(self.roots as *const _, state);
- std::ptr::hash(self.node_paths as *const _, state);
- std::ptr::hash(self.attr_paths as *const _, state);
- }
- // Otherwise, we hash by value
- else {
- self.roots.hash(state);
- self.node_paths.hash(state);
- self.attr_paths.hash(state);
- }
- }
- }
- impl PartialEq for Template {
- fn eq(&self, other: &Self) -> bool {
- // If identical static items are merged, we can compare templates by pointer
- if static_items_merged() {
- std::ptr::eq(self.roots as *const _, other.roots as *const _)
- && std::ptr::eq(self.node_paths as *const _, other.node_paths as *const _)
- && std::ptr::eq(self.attr_paths as *const _, other.attr_paths as *const _)
- }
- // Otherwise, we compare by value
- else {
- self.roots == other.roots
- && self.node_paths == other.node_paths
- && self.attr_paths == other.attr_paths
- }
- }
- }
- #[cfg(feature = "serialize")]
- pub(crate) fn deserialize_string_leaky<'a, 'de, D>(
- deserializer: D,
- ) -> Result<&'static str, D::Error>
- where
- D: serde::Deserializer<'de>,
- {
- use serde::Deserialize;
- let deserialized = String::deserialize(deserializer)?;
- Ok(&*Box::leak(deserialized.into_boxed_str()))
- }
- #[cfg(feature = "serialize")]
- fn deserialize_bytes_leaky<'a, 'de, D>(
- deserializer: D,
- ) -> Result<&'static [&'static [u8]], D::Error>
- where
- D: serde::Deserializer<'de>,
- {
- use serde::Deserialize;
- let deserialized = Vec::<Vec<u8>>::deserialize(deserializer)?;
- let deserialized = deserialized
- .into_iter()
- .map(|v| &*Box::leak(v.into_boxed_slice()))
- .collect::<Vec<_>>();
- Ok(&*Box::leak(deserialized.into_boxed_slice()))
- }
- #[cfg(feature = "serialize")]
- pub(crate) fn deserialize_leaky<'a, 'de, T, D>(deserializer: D) -> Result<&'static [T], D::Error>
- where
- T: serde::Deserialize<'de>,
- D: serde::Deserializer<'de>,
- {
- use serde::Deserialize;
- let deserialized = Box::<[T]>::deserialize(deserializer)?;
- Ok(&*Box::leak(deserialized))
- }
- #[cfg(feature = "serialize")]
- pub(crate) fn deserialize_option_leaky<'a, 'de, D>(
- deserializer: D,
- ) -> Result<Option<&'static str>, D::Error>
- where
- D: serde::Deserializer<'de>,
- {
- use serde::Deserialize;
- let deserialized = Option::<String>::deserialize(deserializer)?;
- Ok(deserialized.map(|deserialized| &*Box::leak(deserialized.into_boxed_str())))
- }
- impl Template {
- /// Is this template worth caching at all, since it's completely runtime?
- ///
- /// There's no point in saving templates that are completely dynamic, since they'll be recreated every time anyway.
- pub fn is_completely_dynamic(&self) -> bool {
- use TemplateNode::*;
- self.roots.iter().all(|root| matches!(root, Dynamic { .. }))
- }
- }
- /// A statically known node in a layout.
- ///
- /// This can be created at compile time, saving the VirtualDom time when diffing the tree
- #[derive(Debug, Clone, Copy, PartialEq, Hash, Eq, PartialOrd, Ord)]
- #[cfg_attr(
- feature = "serialize",
- derive(serde::Serialize, serde::Deserialize),
- serde(tag = "type")
- )]
- pub enum TemplateNode {
- /// An statically known element in the dom.
- ///
- /// In HTML this would be something like `<div id="123"> </div>`
- Element {
- /// The name of the element
- ///
- /// IE for a div, it would be the string "div"
- #[cfg_attr(
- feature = "serialize",
- serde(deserialize_with = "deserialize_string_leaky")
- )]
- tag: StaticStr,
- /// The namespace of the element
- ///
- /// In HTML, this would be a valid URI that defines a namespace for all elements below it
- /// SVG is an example of this namespace
- #[cfg_attr(
- feature = "serialize",
- serde(deserialize_with = "deserialize_option_leaky")
- )]
- namespace: Option<StaticStr>,
- /// A list of possibly dynamic attributes for this element
- ///
- /// An attribute on a DOM node, such as `id="my-thing"` or `href="https://example.com"`.
- #[cfg_attr(
- feature = "serialize",
- serde(deserialize_with = "deserialize_leaky", bound = "")
- )]
- attrs: StaticTemplateAttributeArray,
- /// A list of template nodes that define another set of template nodes
- #[cfg_attr(feature = "serialize", serde(deserialize_with = "deserialize_leaky"))]
- children: StaticTemplateArray,
- },
- /// This template node is just a piece of static text
- Text {
- /// The actual text
- #[cfg_attr(
- feature = "serialize",
- serde(deserialize_with = "deserialize_string_leaky", bound = "")
- )]
- text: StaticStr,
- },
- /// This template node is unknown, and needs to be created at runtime.
- Dynamic {
- /// The index of the dynamic node in the VNode's dynamic_nodes list
- id: usize,
- },
- }
- impl TemplateNode {
- /// Try to load the dynamic node at the given index
- pub fn dynamic_id(&self) -> Option<usize> {
- use TemplateNode::*;
- match self {
- Dynamic { id } => Some(*id),
- _ => None,
- }
- }
- }
- /// A node created at runtime
- ///
- /// This node's index in the DynamicNode list on VNode should match its respective `Dynamic` index
- #[derive(Debug, Clone)]
- pub enum DynamicNode {
- /// A component node
- ///
- /// Most of the time, Dioxus will actually know which component this is as compile time, but the props and
- /// assigned scope are dynamic.
- ///
- /// The actual VComponent can be dynamic between two VNodes, though, allowing implementations to swap
- /// the render function at runtime
- Component(VComponent),
- /// A text node
- Text(VText),
- /// A placeholder
- ///
- /// Used by suspense when a node isn't ready and by fragments that don't render anything
- ///
- /// In code, this is just an ElementId whose initial value is set to 0 upon creation
- Placeholder(VPlaceholder),
- /// A list of VNodes.
- ///
- /// Note that this is not a list of dynamic nodes. These must be VNodes and created through conditional rendering
- /// or iterators.
- Fragment(Vec<VNode>),
- }
- impl DynamicNode {
- /// Convert any item that implements [`IntoDynNode`] into a [`DynamicNode`]
- pub fn make_node<'c, I>(into: impl IntoDynNode<I> + 'c) -> DynamicNode {
- into.into_dyn_node()
- }
- }
- impl Default for DynamicNode {
- fn default() -> Self {
- Self::Placeholder(Default::default())
- }
- }
- /// An instance of a child component
- pub struct VComponent {
- /// The name of this component
- pub name: &'static str,
- /// The raw pointer to the render function
- pub(crate) render_fn: usize,
- /// The props for this component
- pub(crate) props: BoxedAnyProps,
- }
- impl Clone for VComponent {
- fn clone(&self) -> Self {
- Self {
- name: self.name,
- props: self.props.duplicate(),
- render_fn: self.render_fn,
- }
- }
- }
- impl VComponent {
- /// Create a new [`VComponent`] variant
- pub fn new<P, M: 'static>(
- component: impl ComponentFunction<P, M>,
- props: P,
- fn_name: &'static str,
- ) -> Self
- where
- P: Properties + 'static,
- {
- let render_fn = component.fn_ptr();
- let props = Box::new(VProps::new(
- component,
- <P as Properties>::memoize,
- props,
- fn_name,
- ));
- VComponent {
- render_fn,
- name: fn_name,
- props,
- }
- }
- /// Get the [`ScopeId`] this node is mounted to if it's mounted
- ///
- /// This is useful for rendering nodes outside of the VirtualDom, such as in SSR
- ///
- /// Returns [`None`] if the node is not mounted
- pub fn mounted_scope_id(
- &self,
- dynamic_node_index: usize,
- vnode: &VNode,
- dom: &VirtualDom,
- ) -> Option<ScopeId> {
- let mount = vnode.mount.get().as_usize()?;
- let mounts = dom.runtime.mounts.borrow();
- let scope_id = mounts.get(mount)?.mounted_dynamic_nodes[dynamic_node_index];
- Some(ScopeId(scope_id))
- }
- /// Get the scope this node is mounted to if it's mounted
- ///
- /// This is useful for rendering nodes outside of the VirtualDom, such as in SSR
- ///
- /// Returns [`None`] if the node is not mounted
- pub fn mounted_scope<'a>(
- &self,
- dynamic_node_index: usize,
- vnode: &VNode,
- dom: &'a VirtualDom,
- ) -> Option<&'a ScopeState> {
- let mount = vnode.mount.get().as_usize()?;
- let mounts = dom.runtime.mounts.borrow();
- let scope_id = mounts.get(mount)?.mounted_dynamic_nodes[dynamic_node_index];
- dom.scopes.get(scope_id)
- }
- }
- impl std::fmt::Debug for VComponent {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- f.debug_struct("VComponent")
- .field("name", &self.name)
- .finish()
- }
- }
- /// A text node
- #[derive(Clone, Debug)]
- pub struct VText {
- /// The actual text itself
- pub value: String,
- }
- impl VText {
- /// Create a new VText
- pub fn new(value: impl ToString) -> Self {
- Self {
- value: value.to_string(),
- }
- }
- }
- impl From<Arguments<'_>> for VText {
- fn from(args: Arguments) -> Self {
- Self::new(args.to_string())
- }
- }
- /// A placeholder node, used by suspense and fragments
- #[derive(Clone, Debug, Default)]
- #[non_exhaustive]
- pub struct VPlaceholder {}
- /// An attribute of the TemplateNode, created at compile time
- #[derive(Debug, PartialEq, Hash, Eq, PartialOrd, Ord)]
- #[cfg_attr(
- feature = "serialize",
- derive(serde::Serialize, serde::Deserialize),
- serde(tag = "type")
- )]
- pub enum TemplateAttribute {
- /// This attribute is entirely known at compile time, enabling
- Static {
- /// The name of this attribute.
- ///
- /// For example, the `href` attribute in `href="https://example.com"`, would have the name "href"
- #[cfg_attr(
- feature = "serialize",
- serde(deserialize_with = "deserialize_string_leaky", bound = "")
- )]
- name: StaticStr,
- /// The value of this attribute, known at compile time
- ///
- /// Currently this only accepts &str, so values, even if they're known at compile time, are not known
- #[cfg_attr(
- feature = "serialize",
- serde(deserialize_with = "deserialize_string_leaky", bound = "")
- )]
- value: StaticStr,
- /// The namespace of this attribute. Does not exist in the HTML spec
- #[cfg_attr(
- feature = "serialize",
- serde(deserialize_with = "deserialize_option_leaky", bound = "")
- )]
- namespace: Option<StaticStr>,
- },
- /// The attribute in this position is actually determined dynamically at runtime
- ///
- /// This is the index into the dynamic_attributes field on the container VNode
- Dynamic {
- /// The index
- id: usize,
- },
- }
- /// An attribute on a DOM node, such as `id="my-thing"` or `href="https://example.com"`
- #[derive(Debug, Clone, PartialEq)]
- pub struct Attribute {
- /// The name of the attribute.
- pub name: &'static str,
- /// The value of the attribute
- pub value: AttributeValue,
- /// The namespace of the attribute.
- ///
- /// Doesn’t exist in the html spec. Used in Dioxus to denote “style” tags and other attribute groups.
- pub namespace: Option<&'static str>,
- /// An indication of we should always try and set the attribute. Used in controlled components to ensure changes are propagated
- pub volatile: bool,
- }
- impl Attribute {
- /// Create a new [`Attribute`] from a name, value, namespace, and volatile bool
- ///
- /// "Volatile" refers to whether or not Dioxus should always override the value. This helps prevent the UI in
- /// some renderers stay in sync with the VirtualDom's understanding of the world
- pub fn new<T>(
- name: &'static str,
- value: impl IntoAttributeValue<T>,
- namespace: Option<&'static str>,
- volatile: bool,
- ) -> Attribute {
- Attribute {
- name,
- namespace,
- volatile,
- value: value.into_value(),
- }
- }
- /// Create a new deep clone of this attribute
- pub(crate) fn deep_clone(&self) -> Self {
- Attribute {
- name: self.name,
- namespace: self.namespace,
- volatile: self.volatile,
- value: self.value.clone(),
- }
- }
- }
- /// Any of the built-in values that the Dioxus VirtualDom supports as dynamic attributes on elements
- ///
- /// These are built-in to be faster during the diffing process. To use a custom value, use the [`AttributeValue::Any`]
- /// variant.
- #[derive(Clone)]
- pub enum AttributeValue {
- /// Text attribute
- Text(String),
- /// A float
- Float(f64),
- /// Signed integer
- Int(i64),
- /// Boolean
- Bool(bool),
- /// A listener, like "onclick"
- Listener(ListenerCallback),
- /// An arbitrary value that implements PartialEq and is static
- Any(Rc<dyn AnyValue>),
- /// A "none" value, resulting in the removal of an attribute from the dom
- None,
- }
- impl AttributeValue {
- /// Create a new [`AttributeValue`] with the listener variant from a callback
- ///
- /// The callback must be confined to the lifetime of the ScopeState
- pub fn listener<T: 'static>(callback: impl FnMut(Event<T>) + 'static) -> AttributeValue {
- AttributeValue::Listener(ListenerCallback::new(callback).erase())
- }
- /// Create a new [`AttributeValue`] with a value that implements [`AnyValue`]
- pub fn any_value<T: AnyValue>(value: T) -> AttributeValue {
- AttributeValue::Any(Rc::new(value))
- }
- }
- impl std::fmt::Debug for AttributeValue {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- match self {
- Self::Text(arg0) => f.debug_tuple("Text").field(arg0).finish(),
- Self::Float(arg0) => f.debug_tuple("Float").field(arg0).finish(),
- Self::Int(arg0) => f.debug_tuple("Int").field(arg0).finish(),
- Self::Bool(arg0) => f.debug_tuple("Bool").field(arg0).finish(),
- Self::Listener(_) => f.debug_tuple("Listener").finish(),
- Self::Any(_) => f.debug_tuple("Any").finish(),
- Self::None => write!(f, "None"),
- }
- }
- }
- impl PartialEq for AttributeValue {
- fn eq(&self, other: &Self) -> bool {
- match (self, other) {
- (Self::Text(l0), Self::Text(r0)) => l0 == r0,
- (Self::Float(l0), Self::Float(r0)) => l0 == r0,
- (Self::Int(l0), Self::Int(r0)) => l0 == r0,
- (Self::Bool(l0), Self::Bool(r0)) => l0 == r0,
- (Self::Listener(l0), Self::Listener(r0)) => l0 == r0,
- (Self::Any(l0), Self::Any(r0)) => l0.as_ref().any_cmp(r0.as_ref()),
- (Self::None, Self::None) => true,
- _ => false,
- }
- }
- }
- #[doc(hidden)]
- pub trait AnyValue: 'static {
- fn any_cmp(&self, other: &dyn AnyValue) -> bool;
- fn as_any(&self) -> &dyn Any;
- fn type_id(&self) -> TypeId {
- self.as_any().type_id()
- }
- }
- impl<T: Any + PartialEq + 'static> AnyValue for T {
- fn any_cmp(&self, other: &dyn AnyValue) -> bool {
- if let Some(other) = other.as_any().downcast_ref() {
- self == other
- } else {
- false
- }
- }
- fn as_any(&self) -> &dyn Any {
- self
- }
- }
- /// A trait that allows various items to be converted into a dynamic node for the rsx macro
- pub trait IntoDynNode<A = ()> {
- /// Consume this item and produce a DynamicNode
- fn into_dyn_node(self) -> DynamicNode;
- }
- impl IntoDynNode for () {
- fn into_dyn_node(self) -> DynamicNode {
- DynamicNode::default()
- }
- }
- impl IntoDynNode for VNode {
- fn into_dyn_node(self) -> DynamicNode {
- DynamicNode::Fragment(vec![self])
- }
- }
- impl IntoDynNode for DynamicNode {
- fn into_dyn_node(self) -> DynamicNode {
- self
- }
- }
- impl<T: IntoDynNode> IntoDynNode for Option<T> {
- fn into_dyn_node(self) -> DynamicNode {
- match self {
- Some(val) => val.into_dyn_node(),
- None => DynamicNode::default(),
- }
- }
- }
- impl IntoDynNode for &Element {
- fn into_dyn_node(self) -> DynamicNode {
- match self.as_ref() {
- Ok(val) => val.into_dyn_node(),
- _ => DynamicNode::default(),
- }
- }
- }
- impl IntoDynNode for Element {
- fn into_dyn_node(self) -> DynamicNode {
- match self {
- Ok(val) => val.into_dyn_node(),
- _ => DynamicNode::default(),
- }
- }
- }
- impl IntoDynNode for &Option<VNode> {
- fn into_dyn_node(self) -> DynamicNode {
- match self.as_ref() {
- Some(val) => val.clone().into_dyn_node(),
- _ => DynamicNode::default(),
- }
- }
- }
- impl IntoDynNode for &str {
- fn into_dyn_node(self) -> DynamicNode {
- DynamicNode::Text(VText {
- value: self.to_string(),
- })
- }
- }
- impl IntoDynNode for String {
- fn into_dyn_node(self) -> DynamicNode {
- DynamicNode::Text(VText { value: self })
- }
- }
- impl IntoDynNode for Arguments<'_> {
- fn into_dyn_node(self) -> DynamicNode {
- DynamicNode::Text(VText {
- value: self.to_string(),
- })
- }
- }
- impl IntoDynNode for &VNode {
- fn into_dyn_node(self) -> DynamicNode {
- DynamicNode::Fragment(vec![self.clone()])
- }
- }
- pub trait IntoVNode {
- fn into_vnode(self) -> VNode;
- }
- impl IntoVNode for VNode {
- fn into_vnode(self) -> VNode {
- self
- }
- }
- impl IntoVNode for &VNode {
- fn into_vnode(self) -> VNode {
- self.clone()
- }
- }
- impl IntoVNode for Element {
- fn into_vnode(self) -> VNode {
- match self {
- Ok(val) => val.into_vnode(),
- _ => VNode::empty().unwrap(),
- }
- }
- }
- impl IntoVNode for &Element {
- fn into_vnode(self) -> VNode {
- match self {
- Ok(val) => val.into_vnode(),
- _ => VNode::empty().unwrap(),
- }
- }
- }
- impl IntoVNode for Option<VNode> {
- fn into_vnode(self) -> VNode {
- match self {
- Some(val) => val.into_vnode(),
- _ => VNode::empty().unwrap(),
- }
- }
- }
- impl IntoVNode for &Option<VNode> {
- fn into_vnode(self) -> VNode {
- match self.as_ref() {
- Some(val) => val.clone().into_vnode(),
- _ => VNode::empty().unwrap(),
- }
- }
- }
- impl IntoVNode for Option<Element> {
- fn into_vnode(self) -> VNode {
- match self {
- Some(val) => val.into_vnode(),
- _ => VNode::empty().unwrap(),
- }
- }
- }
- impl IntoVNode for &Option<Element> {
- fn into_vnode(self) -> VNode {
- match self.as_ref() {
- Some(val) => val.clone().into_vnode(),
- _ => VNode::empty().unwrap(),
- }
- }
- }
- // Note that we're using the E as a generic but this is never crafted anyways.
- pub struct FromNodeIterator;
- impl<T, I> IntoDynNode<FromNodeIterator> for T
- where
- T: Iterator<Item = I>,
- I: IntoVNode,
- {
- fn into_dyn_node(self) -> DynamicNode {
- let children: Vec<_> = self.into_iter().map(|node| node.into_vnode()).collect();
- if children.is_empty() {
- DynamicNode::default()
- } else {
- DynamicNode::Fragment(children)
- }
- }
- }
- /// A value that can be converted into an attribute value
- pub trait IntoAttributeValue<T = ()> {
- /// Convert into an attribute value
- fn into_value(self) -> AttributeValue;
- }
- impl IntoAttributeValue for AttributeValue {
- fn into_value(self) -> AttributeValue {
- self
- }
- }
- impl IntoAttributeValue for &str {
- fn into_value(self) -> AttributeValue {
- AttributeValue::Text(self.to_string())
- }
- }
- impl IntoAttributeValue for String {
- fn into_value(self) -> AttributeValue {
- AttributeValue::Text(self)
- }
- }
- impl IntoAttributeValue for f32 {
- fn into_value(self) -> AttributeValue {
- AttributeValue::Float(self as _)
- }
- }
- impl IntoAttributeValue for f64 {
- fn into_value(self) -> AttributeValue {
- AttributeValue::Float(self)
- }
- }
- impl IntoAttributeValue for i8 {
- fn into_value(self) -> AttributeValue {
- AttributeValue::Int(self as _)
- }
- }
- impl IntoAttributeValue for i16 {
- fn into_value(self) -> AttributeValue {
- AttributeValue::Int(self as _)
- }
- }
- impl IntoAttributeValue for i32 {
- fn into_value(self) -> AttributeValue {
- AttributeValue::Int(self as _)
- }
- }
- impl IntoAttributeValue for i64 {
- fn into_value(self) -> AttributeValue {
- AttributeValue::Int(self)
- }
- }
- impl IntoAttributeValue for isize {
- fn into_value(self) -> AttributeValue {
- AttributeValue::Int(self as _)
- }
- }
- impl IntoAttributeValue for i128 {
- fn into_value(self) -> AttributeValue {
- AttributeValue::Int(self as _)
- }
- }
- impl IntoAttributeValue for u8 {
- fn into_value(self) -> AttributeValue {
- AttributeValue::Int(self as _)
- }
- }
- impl IntoAttributeValue for u16 {
- fn into_value(self) -> AttributeValue {
- AttributeValue::Int(self as _)
- }
- }
- impl IntoAttributeValue for u32 {
- fn into_value(self) -> AttributeValue {
- AttributeValue::Int(self as _)
- }
- }
- impl IntoAttributeValue for u64 {
- fn into_value(self) -> AttributeValue {
- AttributeValue::Int(self as _)
- }
- }
- impl IntoAttributeValue for usize {
- fn into_value(self) -> AttributeValue {
- AttributeValue::Int(self as _)
- }
- }
- impl IntoAttributeValue for u128 {
- fn into_value(self) -> AttributeValue {
- AttributeValue::Int(self as _)
- }
- }
- impl IntoAttributeValue for bool {
- fn into_value(self) -> AttributeValue {
- AttributeValue::Bool(self)
- }
- }
- impl IntoAttributeValue for Arguments<'_> {
- fn into_value(self) -> AttributeValue {
- AttributeValue::Text(self.to_string())
- }
- }
- impl IntoAttributeValue for Rc<dyn AnyValue> {
- fn into_value(self) -> AttributeValue {
- AttributeValue::Any(self)
- }
- }
- impl<T> IntoAttributeValue for ListenerCallback<T> {
- fn into_value(self) -> AttributeValue {
- AttributeValue::Listener(self.erase())
- }
- }
- impl<T: IntoAttributeValue> IntoAttributeValue for Option<T> {
- fn into_value(self) -> AttributeValue {
- match self {
- Some(val) => val.into_value(),
- None => AttributeValue::None,
- }
- }
- }
- pub struct AnyFmtMarker;
- impl<T> IntoAttributeValue<AnyFmtMarker> for T
- where
- T: DioxusFormattable,
- {
- fn into_value(self) -> AttributeValue {
- AttributeValue::Text(self.format().to_string())
- }
- }
- /// A trait for anything that has a dynamic list of attributes
- pub trait HasAttributes {
- /// Push an attribute onto the list of attributes
- fn push_attribute<T>(
- self,
- name: &'static str,
- ns: Option<&'static str>,
- attr: impl IntoAttributeValue<T>,
- volatile: bool,
- ) -> Self;
- }
|