Skip to main content

dioxus_core/
nodes.rs

1use crate::{
2    DynamicAnchor, DynamicNodeSlot, Element, Event, Properties, ScopeId, Template, VirtualDom,
3    arena::ElementId,
4    events::ListenerCallback,
5    innerlude::{BoxedAnyProps, MountId, ScopeState, VProps},
6    properties::ComponentFunction,
7    view::ViewExt,
8};
9use dioxus_core_types::DioxusFormattable;
10
11use std::ops::Deref;
12use std::rc::Rc;
13use std::{
14    any::{Any, TypeId},
15    fmt::{Arguments, Debug},
16};
17
18/// Runtime node and attribute values that hydrate a static [`Template`].
19pub struct DynamicValues {
20    /// Root key for this render.
21    pub(crate) key: Option<String>,
22
23    /// Dynamic node values in template order.
24    pub(crate) dynamic_nodes: Vec<DynamicNode>,
25
26    /// Dynamic attribute values in template order.
27    pub(crate) dynamic_attrs: Vec<Box<[Attribute]>>,
28}
29
30impl Debug for DynamicValues {
31    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
32        f.debug_struct("DynamicValues").finish_non_exhaustive()
33    }
34}
35
36impl DynamicValues {
37    /// Create a dynamic node/attribute payload.
38    ///
39    /// Each dynamic attribute slot is normalized so its attributes are sorted by
40    /// `(name, namespace)`. Duplicate keys keep their relative input order, giving
41    /// last-wins semantics for spread attributes like `..props.attributes`.
42    #[inline]
43    pub fn from_parts(
44        key: Option<String>,
45        dynamic_nodes: Box<[DynamicNode]>,
46        dynamic_attrs: Box<[Box<[Attribute]>]>,
47    ) -> Self {
48        let mut values = Self {
49            key,
50            dynamic_nodes: dynamic_nodes.into_vec(),
51            dynamic_attrs: dynamic_attrs.into_vec(),
52        };
53        values.normalize();
54        values
55    }
56
57    /// Create an empty dynamic node/attribute payload.
58    pub(crate) fn new() -> Self {
59        Self {
60            key: None,
61            dynamic_nodes: Vec::new(),
62            dynamic_attrs: Vec::new(),
63        }
64    }
65
66    /// Set the root key for this render.
67    pub(crate) fn set_key(&mut self, key: Option<String>) {
68        self.key = key;
69    }
70
71    /// Push a dynamic node slot.
72    pub(crate) fn push_node(&mut self, value: DynamicNode) {
73        self.dynamic_nodes.push(value);
74    }
75
76    /// Push a dynamic attribute slot.
77    ///
78    /// Dynamic attribute slots are normalized by [`Self::normalize`] before the values are stored
79    /// on a [`VNode`].
80    pub(crate) fn push_attrs(&mut self, value: Box<[Attribute]>) {
81        self.dynamic_attrs.push(value);
82    }
83
84    /// Normalize dynamic attribute slots for diffing.
85    pub(crate) fn normalize(&mut self) {
86        for slot in self.dynamic_attrs.iter_mut() {
87            slot.sort_by_key(|attribute| (attribute.name, attribute.namespace));
88        }
89    }
90
91    /// The root key for this render.
92    #[inline]
93    pub fn key(&self) -> Option<&str> {
94        self.key.as_deref()
95    }
96
97    /// The dynamic node values in template order.
98    #[inline]
99    pub fn dynamic_node_values(&self) -> &[DynamicNode] {
100        &self.dynamic_nodes
101    }
102
103    /// The dynamic attribute values in template order.
104    #[inline]
105    pub fn dynamic_attr_values(&self) -> &[Box<[Attribute]>] {
106        &self.dynamic_attrs
107    }
108}
109
110/// A static template with the values rendered for it.
111pub(crate) struct VNodeInner {
112    /// The static template.
113    pub template: Template,
114
115    /// The rendered dynamic node/attribute values.
116    pub view: DynamicValues,
117}
118
119impl Debug for VNodeInner {
120    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
121        f.debug_struct("VNodeInner").finish_non_exhaustive()
122    }
123}
124
125impl Deref for VNodeInner {
126    type Target = DynamicValues;
127
128    fn deref(&self) -> &Self::Target {
129        &self.view
130    }
131}
132
133/// A reference to a template along with any context needed to hydrate it
134///
135/// The dynamic parts of the template are stored separately from the static parts. This allows faster diffing by skipping
136/// static parts of the template.
137#[derive(Clone)]
138pub struct VNode {
139    vnode: Rc<VNodeInner>,
140}
141
142impl Debug for VNode {
143    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
144        f.debug_struct("VNode").finish_non_exhaustive()
145    }
146}
147
148impl Default for VNode {
149    fn default() -> Self {
150        Self::placeholder()
151    }
152}
153
154impl PartialEq for VNode {
155    fn eq(&self, other: &Self) -> bool {
156        Rc::ptr_eq(&self.vnode, &other.vnode)
157    }
158}
159
160impl VNode {
161    /// The static template for this rendered node.
162    pub fn template(&self) -> &Template {
163        &self.vnode.template
164    }
165
166    /// The rendered dynamic node/attribute values.
167    pub fn dynamic_values(&self) -> &DynamicValues {
168        &self.vnode.view
169    }
170
171    /// The dynamic node values in template order.
172    pub fn dynamic_node_values(&self) -> &[DynamicNode] {
173        self.dynamic_values().dynamic_node_values()
174    }
175
176    /// The dynamic attribute values in template order.
177    pub fn dynamic_attr_values(&self) -> &[Box<[Attribute]>] {
178        self.dynamic_values().dynamic_attr_values()
179    }
180
181    /// The root key for this render.
182    pub fn key(&self) -> Option<&str> {
183        self.dynamic_values().key()
184    }
185
186    /// Create a template with no nodes that will be skipped over during diffing
187    pub fn empty() -> Element {
188        Ok(Self::default())
189    }
190
191    /// Create an empty VNode that produces no DOM nodes
192    pub fn placeholder() -> Self {
193        use std::cell::OnceCell;
194        // We can reuse this empty vnode across the same thread to save memory
195        thread_local! {
196            static EMPTY_VNODE: OnceCell<VNode> = const { OnceCell::new() };
197        }
198        EMPTY_VNODE.with(|cell| {
199            cell.get_or_init(move || crate::view::fragment().into_vnode())
200                .clone()
201        })
202    }
203
204    /// Create a new VNode from a static template and dynamic node/attribute payload.
205    pub fn new(template: Template, mut values: DynamicValues) -> Self {
206        values.normalize();
207
208        debug_assert!(
209            values.dynamic_nodes.len()
210                == template
211                    .anchors()
212                    .iter()
213                    .map(|anchor| anchor.nodes().end)
214                    .max()
215                    .unwrap_or_default(),
216            "bad dynamic node count"
217        );
218        debug_assert!(
219            values.dynamic_attrs.len()
220                == template
221                    .anchors()
222                    .iter()
223                    .map(|anchor| anchor.attributes().end)
224                    .max()
225                    .unwrap_or_default(),
226            "bad dynamic attribute count"
227        );
228
229        Self {
230            vnode: Rc::new(VNodeInner {
231                template,
232                view: values,
233            }),
234        }
235    }
236}
237
238#[derive(Clone, Copy, Debug)]
239/// A [`VNode`] paired with the live mount that renders it.
240pub struct MountedVNode<'a> {
241    vnode: &'a VNode,
242    mount: MountId,
243}
244
245impl<'a> MountedVNode<'a> {
246    pub(crate) const fn new(vnode: &'a VNode, mount: MountId) -> Self {
247        Self { vnode, mount }
248    }
249
250    pub(crate) const fn mount(self) -> MountId {
251        self.mount
252    }
253
254    /// Return the underlying vnode.
255    pub const fn vnode(self) -> &'a VNode {
256        self.vnode
257    }
258
259    /// Get the mounted id for a dynamic node.
260    pub fn mounted_dynamic_node(
261        self,
262        slot: DynamicNodeSlot<'a>,
263        dom: &VirtualDom,
264    ) -> Option<ElementId> {
265        let dynamic_node_idx = slot.index();
266        match &self.vnode.dynamic_node_values()[dynamic_node_idx] {
267            DynamicNode::Text(_) => dom
268                .mounted_dynamic_text_node(self.mount, dynamic_node_idx)
269                .map(|id| id.element_id()),
270            _ => None,
271        }
272    }
273
274    /// Get the mounted id for the static template node addressed by a dynamic anchor.
275    pub fn mounted_anchor_node(
276        self,
277        anchor: DynamicAnchor<'a>,
278        dom: &VirtualDom,
279    ) -> Option<ElementId> {
280        dom.mounted_anchor_node(self.mount, anchor.anchor_index())
281            .map(|id| id.element_id())
282    }
283
284    /// Get the mounted id for a static template node by anchor index.
285    pub fn mounted_static_anchor(self, anchor_idx: usize, dom: &VirtualDom) -> Option<ElementId> {
286        dom.mounted_anchor_node(self.mount, anchor_idx)
287            .map(|id| id.element_id())
288    }
289
290    /// Get mounted children for a dynamic fragment.
291    pub fn mounted_fragment_children(
292        self,
293        slot: DynamicNodeSlot<'a>,
294        dom: &VirtualDom,
295    ) -> Vec<MountedVNode<'a>> {
296        let dynamic_node_idx = slot.index();
297        let DynamicNode::Fragment(children) = &self.vnode.dynamic_node_values()[dynamic_node_idx]
298        else {
299            return Vec::new();
300        };
301
302        children
303            .iter()
304            .zip(dom.mounted_fragment_children(self.mount, dynamic_node_idx, children.len()))
305            .map(|(vnode, mount)| MountedVNode::new(vnode, mount))
306            .collect()
307    }
308}
309
310impl Deref for MountedVNode<'_> {
311    type Target = VNode;
312
313    fn deref(&self) -> &Self::Target {
314        self.vnode
315    }
316}
317
318/// A node created at runtime
319///
320/// This node's index in the DynamicNode list on VNode should match its respective `Dynamic` index
321#[derive(Debug, Clone)]
322pub enum DynamicNode {
323    /// A component node
324    ///
325    /// Most of the time, Dioxus will actually know which component this is as compile time, but the props and
326    /// assigned scope are dynamic.
327    ///
328    /// The actual VComponent can be dynamic between two VNodes, though, allowing implementations to swap
329    /// the render function at runtime
330    Component(VComponent),
331
332    /// A text node
333    Text(VText),
334
335    /// A list of VNodes.
336    ///
337    /// Note that this is not a list of dynamic nodes. These must be VNodes and created through conditional rendering
338    /// or iterators.
339    Fragment(Vec<VNode>),
340}
341
342impl DynamicNode {
343    /// Convert any item that implements [`IntoDynNode`] into a [`DynamicNode`]
344    pub fn make_node<'c, I>(into: impl IntoDynNode<I> + 'c) -> DynamicNode {
345        into.into_dyn_node()
346    }
347}
348
349impl Default for DynamicNode {
350    fn default() -> Self {
351        Self::Fragment(Vec::new())
352    }
353}
354
355/// An instance of a child component
356pub struct VComponent {
357    /// The name of this component
358    pub name: &'static str,
359
360    /// The raw pointer to the render function.
361    pub(crate) render_fn: usize,
362
363    /// The rendering lifecycle for this component's scope.
364    pub(crate) driver: Rc<dyn crate::render_driver::RenderDriver>,
365
366    /// The props this component renders from.
367    pub(crate) props: BoxedAnyProps,
368}
369
370impl Clone for VComponent {
371    fn clone(&self) -> Self {
372        Self {
373            name: self.name,
374            render_fn: self.render_fn,
375            driver: self.driver.clone(),
376            props: self.props.duplicate(),
377        }
378    }
379}
380
381impl VComponent {
382    /// Create a new [`VComponent`] variant
383    pub fn new<P, M: 'static>(
384        component: impl ComponentFunction<P, M>,
385        props: P,
386        fn_name: &'static str,
387    ) -> Self
388    where
389        P: Properties + 'static,
390    {
391        let render_fn = component.fn_ptr();
392        let props = Box::new(VProps::new(
393            component,
394            <P as Properties>::memoize,
395            props,
396            fn_name,
397        ));
398        Self::new_with_driver(
399            fn_name,
400            render_fn,
401            Rc::new(crate::render_driver::BodyDriver),
402            props,
403        )
404    }
405
406    /// Create a new [`VComponent`] whose scope is rendered by `driver`.
407    pub(crate) fn new_with_driver(
408        fn_name: &'static str,
409        render_fn: usize,
410        driver: Rc<dyn crate::render_driver::RenderDriver>,
411        props: BoxedAnyProps,
412    ) -> Self {
413        VComponent {
414            name: fn_name,
415            render_fn,
416            driver,
417            props,
418        }
419    }
420
421    /// Get the [`ScopeId`] this node is mounted to if it's mounted
422    ///
423    /// This is useful for rendering nodes outside of the VirtualDom, such as in SSR
424    ///
425    /// Returns [`None`] if the node is not mounted
426    pub fn mounted_scope_id(
427        &self,
428        slot: DynamicNodeSlot<'_>,
429        vnode: MountedVNode<'_>,
430        dom: &VirtualDom,
431    ) -> Option<ScopeId> {
432        dom.mounted_dynamic_component_scope(vnode.mount(), slot.index())
433    }
434
435    /// Get the scope this node is mounted to if it's mounted
436    ///
437    /// This is useful for rendering nodes outside of the VirtualDom, such as in SSR
438    ///
439    /// Returns [`None`] if the node is not mounted
440    pub fn mounted_scope<'a>(
441        &self,
442        slot: DynamicNodeSlot<'_>,
443        vnode: MountedVNode<'_>,
444        dom: &'a VirtualDom,
445    ) -> Option<&'a ScopeState> {
446        let scope_id = dom.mounted_dynamic_component_scope(vnode.mount(), slot.index())?;
447
448        dom.scopes.get(scope_id.index())
449    }
450}
451
452impl std::fmt::Debug for VComponent {
453    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
454        f.debug_struct("VComponent")
455            .field("name", &self.name)
456            .finish()
457    }
458}
459
460/// A text node
461#[derive(Clone, Debug)]
462pub struct VText {
463    /// The actual text itself
464    pub value: String,
465}
466
467impl VText {
468    /// Create a new VText
469    #[inline]
470    pub fn new(value: impl ToString) -> Self {
471        Self {
472            value: value.to_string(),
473        }
474    }
475}
476
477impl From<Arguments<'_>> for VText {
478    fn from(args: Arguments) -> Self {
479        Self::new(args.to_string())
480    }
481}
482
483/// An attribute on a DOM node, such as `id="my-thing"` or `href="https://example.com"`
484#[derive(Debug, Clone, PartialEq)]
485pub struct Attribute {
486    /// The name of the attribute.
487    pub name: &'static str,
488
489    /// The value of the attribute
490    pub value: AttributeValue,
491
492    /// The namespace of the attribute.
493    ///
494    /// Doesn’t exist in the html spec. Used in Dioxus to denote “style” tags and other attribute groups.
495    pub namespace: Option<&'static str>,
496
497    /// An indication of we should always try and set the attribute. Used in controlled components to ensure changes are propagated
498    pub volatile: bool,
499}
500
501impl Attribute {
502    /// Create a new [`Attribute`] from a name, value, namespace, and volatile bool
503    ///
504    /// "Volatile" refers to whether or not Dioxus should always override the value. This helps prevent the UI in
505    /// some renderers stay in sync with the VirtualDom's understanding of the world
506    pub fn new<T>(
507        name: &'static str,
508        value: impl IntoAttributeValue<T>,
509        namespace: Option<&'static str>,
510        volatile: bool,
511    ) -> Attribute {
512        Attribute {
513            name,
514            namespace,
515            volatile,
516            value: value.into_value(),
517        }
518    }
519}
520
521/// Any of the built-in values that the Dioxus VirtualDom supports as dynamic attributes on elements
522///
523/// These are built-in to be faster during the diffing process. To use a custom value, use the [`AttributeValue::Any`]
524/// variant.
525#[derive(Clone)]
526pub enum AttributeValue {
527    /// Text attribute
528    Text(String),
529
530    /// A float
531    Float(f64),
532
533    /// Signed integer
534    Int(i64),
535
536    /// Boolean
537    Bool(bool),
538
539    /// A listener, like "onclick"
540    Listener(ListenerCallback),
541
542    /// An arbitrary value that implements PartialEq and is static
543    Any(Rc<dyn AnyValue>),
544
545    /// A "none" value, resulting in the removal of an attribute from the dom
546    None,
547}
548
549impl AttributeValue {
550    /// Create a new [`AttributeValue`] with the listener variant from a callback
551    ///
552    /// The callback must be confined to the lifetime of the ScopeState
553    pub fn listener<T: 'static>(callback: impl FnMut(Event<T>) + 'static) -> AttributeValue {
554        AttributeValue::Listener(ListenerCallback::new(callback).erase())
555    }
556
557    /// Create a new [`AttributeValue`] with a value that implements [`AnyValue`]
558    pub fn any_value<T: AnyValue>(value: T) -> AttributeValue {
559        AttributeValue::Any(Rc::new(value))
560    }
561}
562
563impl std::fmt::Debug for AttributeValue {
564    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
565        match self {
566            Self::Text(arg0) => f.debug_tuple("Text").field(arg0).finish(),
567            Self::Float(arg0) => f.debug_tuple("Float").field(arg0).finish(),
568            Self::Int(arg0) => f.debug_tuple("Int").field(arg0).finish(),
569            Self::Bool(arg0) => f.debug_tuple("Bool").field(arg0).finish(),
570            Self::Listener(_) => f.debug_tuple("Listener").finish(),
571            Self::Any(_) => f.debug_tuple("Any").finish(),
572            Self::None => write!(f, "None"),
573        }
574    }
575}
576
577impl PartialEq for AttributeValue {
578    fn eq(&self, other: &Self) -> bool {
579        match (self, other) {
580            (Self::Text(l0), Self::Text(r0)) => l0 == r0,
581            (Self::Float(l0), Self::Float(r0)) => l0 == r0,
582            (Self::Int(l0), Self::Int(r0)) => l0 == r0,
583            (Self::Bool(l0), Self::Bool(r0)) => l0 == r0,
584            (Self::Listener(l0), Self::Listener(r0)) => l0 == r0,
585            (Self::Any(l0), Self::Any(r0)) => l0.as_ref().any_cmp(r0.as_ref()),
586            (Self::None, Self::None) => true,
587            _ => false,
588        }
589    }
590}
591
592/// A borrowed attribute value, as passed to [`WriteMutations::set_attribute`](crate::WriteMutations::set_attribute)
593///
594/// Listeners are never set as attributes, so unlike [`AttributeValue`] this has no listener variant.
595#[derive(Clone, Copy)]
596pub enum BorrowedAttributeValue<'a> {
597    /// Text attribute
598    Text(&'a str),
599
600    /// A float
601    Float(f64),
602
603    /// Signed integer
604    Int(i64),
605
606    /// Boolean
607    Bool(bool),
608
609    /// An arbitrary value that implements PartialEq and is static
610    Any(&'a Rc<dyn AnyValue>),
611
612    /// A "none" value, resulting in the removal of an attribute from the dom
613    None,
614}
615
616impl std::fmt::Debug for BorrowedAttributeValue<'_> {
617    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
618        match self {
619            Self::Text(arg0) => f.debug_tuple("Text").field(arg0).finish(),
620            Self::Float(arg0) => f.debug_tuple("Float").field(arg0).finish(),
621            Self::Int(arg0) => f.debug_tuple("Int").field(arg0).finish(),
622            Self::Bool(arg0) => f.debug_tuple("Bool").field(arg0).finish(),
623            Self::Any(_) => f.debug_tuple("Any").finish(),
624            Self::None => write!(f, "None"),
625        }
626    }
627}
628
629#[doc(hidden)]
630pub trait AnyValue: 'static {
631    /// Compare this value with another erased value.
632    fn any_cmp(&self, other: &dyn AnyValue) -> bool;
633
634    /// Return this value as [`Any`] for downcasting.
635    fn as_any(&self) -> &dyn Any;
636
637    /// Return the underlying value's [`TypeId`].
638    fn type_id(&self) -> TypeId {
639        self.as_any().type_id()
640    }
641}
642
643impl<T: Any + PartialEq + 'static> AnyValue for T {
644    fn any_cmp(&self, other: &dyn AnyValue) -> bool {
645        if let Some(other) = other.as_any().downcast_ref() {
646            self == other
647        } else {
648            false
649        }
650    }
651
652    fn as_any(&self) -> &dyn Any {
653        self
654    }
655}
656
657/// A trait that allows various items to be converted into a dynamic node for the rsx macro
658pub trait IntoDynNode<A = ()> {
659    /// Consume this item and produce a DynamicNode
660    fn into_dyn_node(self) -> DynamicNode;
661}
662
663impl IntoDynNode for () {
664    #[inline]
665    fn into_dyn_node(self) -> DynamicNode {
666        DynamicNode::default()
667    }
668}
669impl IntoDynNode for VNode {
670    #[inline]
671    fn into_dyn_node(self) -> DynamicNode {
672        DynamicNode::Fragment(vec![self])
673    }
674}
675impl IntoDynNode for DynamicNode {
676    #[inline]
677    fn into_dyn_node(self) -> DynamicNode {
678        self
679    }
680}
681impl<T: IntoDynNode> IntoDynNode for Option<T> {
682    #[inline]
683    fn into_dyn_node(self) -> DynamicNode {
684        match self {
685            Some(val) => val.into_dyn_node(),
686            None => DynamicNode::default(),
687        }
688    }
689}
690impl IntoDynNode for &Element {
691    #[inline]
692    fn into_dyn_node(self) -> DynamicNode {
693        match self.as_ref() {
694            Ok(val) => val.into_dyn_node(),
695            _ => DynamicNode::default(),
696        }
697    }
698}
699impl IntoDynNode for Element {
700    #[inline]
701    fn into_dyn_node(self) -> DynamicNode {
702        match self {
703            Ok(val) => val.into_dyn_node(),
704            _ => DynamicNode::default(),
705        }
706    }
707}
708impl IntoDynNode for &Option<VNode> {
709    #[inline]
710    fn into_dyn_node(self) -> DynamicNode {
711        match self.as_ref() {
712            Some(val) => val.clone().into_dyn_node(),
713            _ => DynamicNode::default(),
714        }
715    }
716}
717impl IntoDynNode for &str {
718    #[inline]
719    fn into_dyn_node(self) -> DynamicNode {
720        DynamicNode::Text(VText {
721            value: self.to_string(),
722        })
723    }
724}
725impl IntoDynNode for String {
726    #[inline]
727    fn into_dyn_node(self) -> DynamicNode {
728        DynamicNode::Text(VText { value: self })
729    }
730}
731impl IntoDynNode for Arguments<'_> {
732    #[inline]
733    fn into_dyn_node(self) -> DynamicNode {
734        DynamicNode::Text(VText {
735            value: self.to_string(),
736        })
737    }
738}
739impl IntoDynNode for &VNode {
740    #[inline]
741    fn into_dyn_node(self) -> DynamicNode {
742        DynamicNode::Fragment(vec![self.clone()])
743    }
744}
745
746/// Convert a value into a [`VNode`].
747pub trait IntoVNode {
748    /// Convert this value into a [`VNode`].
749    fn into_vnode(self) -> VNode;
750}
751impl IntoVNode for VNode {
752    #[inline]
753    fn into_vnode(self) -> VNode {
754        self
755    }
756}
757impl IntoVNode for &VNode {
758    #[inline]
759    fn into_vnode(self) -> VNode {
760        self.clone()
761    }
762}
763impl IntoVNode for Element {
764    #[inline]
765    fn into_vnode(self) -> VNode {
766        match self {
767            Ok(val) => val.into_vnode(),
768            _ => VNode::default(),
769        }
770    }
771}
772impl IntoVNode for &Element {
773    #[inline]
774    fn into_vnode(self) -> VNode {
775        match self {
776            Ok(val) => val.into_vnode(),
777            _ => VNode::default(),
778        }
779    }
780}
781impl IntoVNode for Option<VNode> {
782    #[inline]
783    fn into_vnode(self) -> VNode {
784        match self {
785            Some(val) => val.into_vnode(),
786            _ => VNode::default(),
787        }
788    }
789}
790impl IntoVNode for &Option<VNode> {
791    #[inline]
792    fn into_vnode(self) -> VNode {
793        match self.as_ref() {
794            Some(val) => val.clone().into_vnode(),
795            _ => VNode::default(),
796        }
797    }
798}
799impl IntoVNode for Option<Element> {
800    #[inline]
801    fn into_vnode(self) -> VNode {
802        match self {
803            Some(val) => val.into_vnode(),
804            _ => VNode::default(),
805        }
806    }
807}
808impl IntoVNode for &Option<Element> {
809    #[inline]
810    fn into_vnode(self) -> VNode {
811        match self.as_ref() {
812            Some(val) => val.clone().into_vnode(),
813            _ => VNode::default(),
814        }
815    }
816}
817
818// Note that we're using the E as a generic but this is never crafted anyways.
819pub struct FromNodeIterator;
820impl<T, I> IntoDynNode<FromNodeIterator> for T
821where
822    T: Iterator<Item = I>,
823    I: IntoVNode,
824{
825    #[inline]
826    fn into_dyn_node(self) -> DynamicNode {
827        DynamicNode::Fragment(self.into_iter().map(|node| node.into_vnode()).collect())
828    }
829}
830
831/// A value that can be converted into an attribute value
832pub trait IntoAttributeValue<T = ()> {
833    /// Convert into an attribute value
834    fn into_value(self) -> AttributeValue;
835}
836
837impl IntoAttributeValue for AttributeValue {
838    fn into_value(self) -> AttributeValue {
839        self
840    }
841}
842
843impl IntoAttributeValue for &str {
844    fn into_value(self) -> AttributeValue {
845        AttributeValue::Text(self.to_string())
846    }
847}
848
849impl IntoAttributeValue for String {
850    fn into_value(self) -> AttributeValue {
851        AttributeValue::Text(self)
852    }
853}
854
855macro_rules! impl_float_attribute_value {
856    ($($ty:ty),* $(,)?) => {
857        $(
858            impl IntoAttributeValue for $ty {
859                fn into_value(self) -> AttributeValue {
860                    AttributeValue::Float(self as _)
861                }
862            }
863        )*
864    };
865}
866
867macro_rules! impl_int_attribute_value {
868    ($($ty:ty),* $(,)?) => {
869        $(
870            impl IntoAttributeValue for $ty {
871                fn into_value(self) -> AttributeValue {
872                    AttributeValue::Int(self as _)
873                }
874            }
875        )*
876    };
877}
878
879impl_float_attribute_value!(f32, f64);
880impl_int_attribute_value!(i8, i16, i32, i64, isize, i128);
881impl_int_attribute_value!(u8, u16, u32, u64, usize, u128);
882
883impl IntoAttributeValue for bool {
884    fn into_value(self) -> AttributeValue {
885        AttributeValue::Bool(self)
886    }
887}
888
889impl IntoAttributeValue for Arguments<'_> {
890    fn into_value(self) -> AttributeValue {
891        AttributeValue::Text(self.to_string())
892    }
893}
894
895impl IntoAttributeValue for Rc<dyn AnyValue> {
896    fn into_value(self) -> AttributeValue {
897        AttributeValue::Any(self)
898    }
899}
900
901impl<T> IntoAttributeValue for ListenerCallback<T> {
902    fn into_value(self) -> AttributeValue {
903        AttributeValue::Listener(self.erase())
904    }
905}
906
907impl<T: IntoAttributeValue> IntoAttributeValue for Option<T> {
908    fn into_value(self) -> AttributeValue {
909        match self {
910            Some(val) => val.into_value(),
911            None => AttributeValue::None,
912        }
913    }
914}
915
916impl<T: ToOwned<Owned = R>, R: IntoAttributeValue> IntoAttributeValue for &T {
917    fn into_value(self) -> AttributeValue {
918        self.to_owned().into_value()
919    }
920}
921
922#[doc(hidden)]
923pub struct AnyFmtMarker;
924impl<T> IntoAttributeValue<AnyFmtMarker> for T
925where
926    T: DioxusFormattable,
927{
928    fn into_value(self) -> AttributeValue {
929        AttributeValue::Text(self.format().to_string())
930    }
931}
932
933/// A trait for anything that has a dynamic list of attributes
934pub trait HasAttributes {
935    /// Push an attribute onto the list of attributes
936    fn push_attribute<T>(
937        self,
938        name: &'static str,
939        ns: Option<&'static str>,
940        attr: impl IntoAttributeValue<T>,
941        volatile: bool,
942    ) -> Self;
943}