freya_native_core/
node.rs

1//! Items related to Nodes in the RealDom
2
3use std::{
4    any::Any,
5    fmt::Debug,
6};
7
8use rustc_hash::{
9    FxHashMap,
10    FxHashSet,
11};
12use shipyard::Component;
13
14use crate::{
15    events::EventName,
16    prelude::AttributeName,
17    tags::TagName,
18};
19
20/// A element node in the RealDom
21#[derive(Debug, Clone)]
22pub struct ElementNode<V: FromAnyValue = ()> {
23    /// The tag name of the element
24    pub tag: TagName,
25    /// The attributes of the element
26    pub attributes: FxHashMap<AttributeName, OwnedAttributeValue<V>>,
27    /// The events the element is listening for
28    pub listeners: FxHashSet<EventName>,
29}
30
31/// A type of node with data specific to the node type.
32#[derive(Debug, Clone, Component)]
33pub enum NodeType<V: FromAnyValue = ()> {
34    /// A text node
35    Text(String),
36    /// An element node
37    Element(ElementNode<V>),
38    /// A placeholder node. This can be used as a cheaper placeholder for a node that will be created later
39    Placeholder,
40}
41
42impl<V: FromAnyValue> NodeType<V> {
43    pub fn is_visible_element(&self) -> bool {
44        if let NodeType::Element(ElementNode { tag, .. }) = self {
45            // No need to consider text spans
46            if tag.has_intrinsic_layout() {
47                return true;
48            }
49        }
50
51        false
52    }
53
54    pub fn is_text(&self) -> bool {
55        matches!(self, Self::Text(..))
56    }
57
58    pub fn is_element(&self) -> bool {
59        matches!(self, Self::Element(..))
60    }
61
62    pub fn is_placeholder(&self) -> bool {
63        matches!(self, Self::Placeholder)
64    }
65
66    #[inline]
67    pub fn tag(&self) -> Option<&TagName> {
68        match self {
69            Self::Element(ElementNode { tag, .. }) => Some(tag),
70            _ => None,
71        }
72    }
73
74    pub fn text(&self) -> Option<&str> {
75        match self {
76            Self::Text(text) => Some(text.as_str()),
77            _ => None,
78        }
79    }
80}
81
82impl<V: FromAnyValue, S: Into<String>> From<S> for NodeType<V> {
83    fn from(text: S) -> Self {
84        Self::Text(text.into())
85    }
86}
87
88impl<V: FromAnyValue> From<ElementNode<V>> for NodeType<V> {
89    fn from(element: ElementNode<V>) -> Self {
90        Self::Element(element)
91    }
92}
93
94/// An attribute on a DOM node, such as `id="my-thing"` or
95/// `href="https://example.com"`.
96#[derive(Clone, Copy, Debug)]
97pub struct OwnedAttributeView<'a, V: FromAnyValue = ()> {
98    /// The discription of the attribute.
99    pub attribute: &'a AttributeName,
100
101    /// The value of the attribute.
102    pub value: &'a OwnedAttributeValue<V>,
103}
104
105/// The value of an attribute on a DOM node. This contains non-text values to allow users to skip parsing attribute values in some cases.
106#[derive(Clone)]
107pub enum OwnedAttributeValue<V: FromAnyValue = ()> {
108    /// A string value. This is the most common type of attribute.
109    Text(String),
110    /// A floating point value.
111    Float(f64),
112    /// An integer value.
113    Int(i64),
114    /// A boolean value.
115    Bool(bool),
116    /// A custom value specific to the renderer
117    Custom(V),
118}
119
120impl<V: FromAnyValue> From<String> for OwnedAttributeValue<V> {
121    fn from(value: String) -> Self {
122        Self::Text(value)
123    }
124}
125
126impl<V: FromAnyValue> From<f64> for OwnedAttributeValue<V> {
127    fn from(value: f64) -> Self {
128        Self::Float(value)
129    }
130}
131
132impl<V: FromAnyValue> From<i64> for OwnedAttributeValue<V> {
133    fn from(value: i64) -> Self {
134        Self::Int(value)
135    }
136}
137
138impl<V: FromAnyValue> From<bool> for OwnedAttributeValue<V> {
139    fn from(value: bool) -> Self {
140        Self::Bool(value)
141    }
142}
143
144impl<V: FromAnyValue> From<V> for OwnedAttributeValue<V> {
145    fn from(value: V) -> Self {
146        Self::Custom(value)
147    }
148}
149
150/// Something that can be converted from a borrowed [Any] value.
151pub trait FromAnyValue: Clone + 'static {
152    /// Convert from an [Any] value.
153    fn from_any_value(value: &dyn Any) -> Self;
154}
155
156impl FromAnyValue for () {
157    fn from_any_value(_: &dyn Any) -> Self {}
158}
159
160impl<V: FromAnyValue> Debug for OwnedAttributeValue<V> {
161    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
162        match self {
163            Self::Text(arg0) => f.debug_tuple("Text").field(arg0).finish(),
164            Self::Float(arg0) => f.debug_tuple("Float").field(arg0).finish(),
165            Self::Int(arg0) => f.debug_tuple("Int").field(arg0).finish(),
166            Self::Bool(arg0) => f.debug_tuple("Bool").field(arg0).finish(),
167            Self::Custom(_) => f.debug_tuple("Any").finish(),
168        }
169    }
170}
171
172impl<V: FromAnyValue> From<&dioxus_core::AttributeValue> for OwnedAttributeValue<V> {
173    fn from(value: &dioxus_core::AttributeValue) -> Self {
174        match value {
175            dioxus_core::AttributeValue::Text(text) => Self::Text(text.clone()),
176            dioxus_core::AttributeValue::Float(float) => Self::Float(*float),
177            dioxus_core::AttributeValue::Int(int) => Self::Int(*int),
178            dioxus_core::AttributeValue::Bool(bool) => Self::Bool(*bool),
179            dioxus_core::AttributeValue::Any(any) => Self::Custom(V::from_any_value(any.as_any())),
180            dioxus_core::AttributeValue::None => panic!("None attribute values result in removing the attribute, not converting it to a None value."),
181            _ => panic!("Unsupported attribute value type"),
182        }
183    }
184}
185
186impl<V: FromAnyValue> OwnedAttributeValue<V> {
187    /// Attempt to convert the attribute value to a string.
188    pub fn as_text(&self) -> Option<&str> {
189        match self {
190            OwnedAttributeValue::Text(text) => Some(text),
191            _ => None,
192        }
193    }
194
195    /// Attempt to convert the attribute value to a float.
196    pub fn as_float(&self) -> Option<f64> {
197        match self {
198            OwnedAttributeValue::Float(float) => Some(*float),
199            OwnedAttributeValue::Int(int) => Some(*int as f64),
200            _ => None,
201        }
202    }
203
204    /// Attempt to convert the attribute value to an integer.
205    pub fn as_int(&self) -> Option<i64> {
206        match self {
207            OwnedAttributeValue::Float(float) => Some(*float as i64),
208            OwnedAttributeValue::Int(int) => Some(*int),
209            _ => None,
210        }
211    }
212
213    /// Attempt to convert the attribute value to a boolean.
214    pub fn as_bool(&self) -> Option<bool> {
215        match self {
216            OwnedAttributeValue::Bool(bool) => Some(*bool),
217            _ => None,
218        }
219    }
220
221    /// Attempt to convert the attribute value to a custom value.
222    pub fn as_custom(&self) -> Option<&V> {
223        match self {
224            OwnedAttributeValue::Custom(custom) => Some(custom),
225            _ => None,
226        }
227    }
228}