nodes.rs 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889
  1. use crate::any_props::{BoxedAnyProps, VProps};
  2. use crate::innerlude::{ElementRef, EventHandler, MountId, ScopeState};
  3. use crate::properties::ComponentFunction;
  4. use crate::{arena::ElementId, Element, Event};
  5. use crate::{Properties, VirtualDom};
  6. use std::ops::Deref;
  7. use std::rc::Rc;
  8. use std::vec;
  9. use std::{
  10. any::{Any, TypeId},
  11. cell::Cell,
  12. fmt::{Arguments, Debug},
  13. };
  14. pub type TemplateId = &'static str;
  15. /// The actual state of the component's most recent computation
  16. ///
  17. /// Because Dioxus accepts components in the form of `async fn(Scope) -> Result<VNode>`, we need to support both
  18. /// sync and async versions.
  19. ///
  20. /// Dioxus will do its best to immediately resolve any async components into a regular Element, but as an implementor
  21. /// you might need to handle the case where there's no node immediately ready.
  22. pub enum RenderReturn {
  23. /// A currently-available element
  24. Ready(VNode),
  25. /// The component aborted rendering early. It might've thrown an error.
  26. ///
  27. /// In its place we've produced a placeholder to locate its spot in the dom when it recovers.
  28. Aborted(VNode),
  29. }
  30. impl Clone for RenderReturn {
  31. fn clone(&self) -> Self {
  32. match self {
  33. RenderReturn::Ready(node) => RenderReturn::Ready(node.clone_mounted()),
  34. RenderReturn::Aborted(node) => RenderReturn::Aborted(node.clone_mounted()),
  35. }
  36. }
  37. }
  38. impl Default for RenderReturn {
  39. fn default() -> Self {
  40. RenderReturn::Aborted(VNode::placeholder())
  41. }
  42. }
  43. impl Deref for RenderReturn {
  44. type Target = VNode;
  45. fn deref(&self) -> &Self::Target {
  46. match self {
  47. RenderReturn::Ready(node) | RenderReturn::Aborted(node) => node,
  48. }
  49. }
  50. }
  51. /// The information about the
  52. #[derive(Debug)]
  53. pub(crate) struct VNodeMount {
  54. /// The parent of this node
  55. pub parent: Option<ElementRef>,
  56. /// A back link to the original node
  57. pub node: VNode,
  58. /// The IDs for the roots of this template - to be used when moving the template around and removing it from
  59. /// the actual Dom
  60. pub root_ids: Box<[ElementId]>,
  61. /// The element in the DOM that each attribute is mounted to
  62. pub(crate) mounted_attributes: Box<[ElementId]>,
  63. /// For components: This is the ScopeId the component is mounted to
  64. /// For other dynamic nodes: This is element in the DOM that each dynamic node is mounted to
  65. pub(crate) mounted_dynamic_nodes: Box<[usize]>,
  66. }
  67. /// A reference to a template along with any context needed to hydrate it
  68. ///
  69. /// The dynamic parts of the template are stored separately from the static parts. This allows faster diffing by skipping
  70. /// static parts of the template.
  71. #[derive(Debug)]
  72. pub struct VNodeInner {
  73. /// The key given to the root of this template.
  74. ///
  75. /// In fragments, this is the key of the first child. In other cases, it is the key of the root.
  76. pub key: Option<String>,
  77. /// The static nodes and static descriptor of the template
  78. pub template: Cell<Template>,
  79. /// The dynamic parts of the template
  80. pub dynamic_nodes: Box<[DynamicNode]>,
  81. /// The dynamic parts of the template
  82. pub dynamic_attrs: Box<[Attribute]>,
  83. }
  84. /// A reference to a template along with any context needed to hydrate it
  85. ///
  86. /// The dynamic parts of the template are stored separately from the static parts. This allows faster diffing by skipping
  87. /// static parts of the template.
  88. #[derive(Debug)]
  89. pub struct VNode {
  90. vnode: Rc<VNodeInner>,
  91. /// The mount information for this template
  92. pub(crate) mount: Cell<MountId>,
  93. }
  94. impl Clone for VNode {
  95. fn clone(&self) -> Self {
  96. Self {
  97. vnode: self.vnode.clone(),
  98. mount: Default::default(),
  99. }
  100. }
  101. }
  102. impl PartialEq for VNode {
  103. fn eq(&self, other: &Self) -> bool {
  104. Rc::ptr_eq(&self.vnode, &other.vnode)
  105. }
  106. }
  107. impl Deref for VNode {
  108. type Target = VNodeInner;
  109. fn deref(&self) -> &Self::Target {
  110. &self.vnode
  111. }
  112. }
  113. impl VNode {
  114. /// Clone the element while retaining the mount information of the node
  115. pub(crate) fn clone_mounted(&self) -> Self {
  116. Self {
  117. vnode: self.vnode.clone(),
  118. mount: self.mount.clone(),
  119. }
  120. }
  121. /// Create a template with no nodes that will be skipped over during diffing
  122. pub fn empty() -> Element {
  123. Some(Self {
  124. vnode: Rc::new(VNodeInner {
  125. key: None,
  126. dynamic_nodes: Box::new([]),
  127. dynamic_attrs: Box::new([]),
  128. template: Cell::new(Template {
  129. name: "dioxus-empty",
  130. roots: &[],
  131. node_paths: &[],
  132. attr_paths: &[],
  133. }),
  134. }),
  135. mount: Default::default(),
  136. })
  137. }
  138. /// Create a template with a single placeholder node
  139. pub fn placeholder() -> Self {
  140. Self {
  141. vnode: Rc::new(VNodeInner {
  142. key: None,
  143. dynamic_nodes: Box::new([DynamicNode::Placeholder(Default::default())]),
  144. dynamic_attrs: Box::new([]),
  145. template: Cell::new(Template {
  146. name: "dioxus-placeholder",
  147. roots: &[TemplateNode::Dynamic { id: 0 }],
  148. node_paths: &[&[]],
  149. attr_paths: &[],
  150. }),
  151. }),
  152. mount: Default::default(),
  153. }
  154. }
  155. /// Create a new VNode
  156. pub fn new(
  157. key: Option<String>,
  158. template: Template,
  159. dynamic_nodes: Box<[DynamicNode]>,
  160. dynamic_attrs: Box<[Attribute]>,
  161. ) -> Self {
  162. Self {
  163. vnode: Rc::new(VNodeInner {
  164. key,
  165. template: Cell::new(template),
  166. dynamic_nodes,
  167. dynamic_attrs,
  168. }),
  169. mount: Default::default(),
  170. }
  171. }
  172. /// Load a dynamic root at the given index
  173. ///
  174. /// Returns [`None`] if the root is actually a static node (Element/Text)
  175. pub fn dynamic_root(&self, idx: usize) -> Option<&DynamicNode> {
  176. match &self.template.get().roots[idx] {
  177. TemplateNode::Element { .. } | TemplateNode::Text { text: _ } => None,
  178. TemplateNode::Dynamic { id } | TemplateNode::DynamicText { id } => {
  179. Some(&self.dynamic_nodes[*id])
  180. }
  181. }
  182. }
  183. }
  184. /// A static layout of a UI tree that describes a set of dynamic and static nodes.
  185. ///
  186. /// This is the core innovation in Dioxus. Most UIs are made of static nodes, yet participate in diffing like any
  187. /// dynamic node. This struct can be created at compile time. It promises that its name is unique, allow Dioxus to use
  188. /// its static description of the UI to skip immediately to the dynamic nodes during diffing.
  189. ///
  190. /// For this to work properly, the [`Template::name`] *must* be unique across your entire project. This can be done via variety of
  191. /// ways, with the suggested approach being the unique code location (file, line, col, etc).
  192. #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
  193. #[derive(Debug, Clone, Copy, PartialEq, Hash, Eq, PartialOrd, Ord)]
  194. pub struct Template {
  195. /// The name of the template. This must be unique across your entire program for template diffing to work properly
  196. ///
  197. /// If two templates have the same name, it's likely that Dioxus will panic when diffing.
  198. #[cfg_attr(
  199. feature = "serialize",
  200. serde(deserialize_with = "deserialize_string_leaky")
  201. )]
  202. pub name: &'static str,
  203. /// The list of template nodes that make up the template
  204. ///
  205. /// Unlike react, calls to `rsx!` can have multiple roots. This list supports that paradigm.
  206. #[cfg_attr(feature = "serialize", serde(deserialize_with = "deserialize_leaky"))]
  207. pub roots: &'static [TemplateNode],
  208. /// The paths of each node relative to the root of the template.
  209. ///
  210. /// These will be one segment shorter than the path sent to the renderer since those paths are relative to the
  211. /// topmost element, not the `roots` field.
  212. #[cfg_attr(
  213. feature = "serialize",
  214. serde(deserialize_with = "deserialize_bytes_leaky")
  215. )]
  216. pub node_paths: &'static [&'static [u8]],
  217. /// The paths of each dynamic attribute relative to the root of the template
  218. ///
  219. /// These will be one segment shorter than the path sent to the renderer since those paths are relative to the
  220. /// topmost element, not the `roots` field.
  221. #[cfg_attr(
  222. feature = "serialize",
  223. serde(deserialize_with = "deserialize_bytes_leaky")
  224. )]
  225. pub attr_paths: &'static [&'static [u8]],
  226. }
  227. #[cfg(feature = "serialize")]
  228. fn deserialize_string_leaky<'a, 'de, D>(deserializer: D) -> Result<&'a str, D::Error>
  229. where
  230. D: serde::Deserializer<'de>,
  231. {
  232. use serde::Deserialize;
  233. let deserialized = String::deserialize(deserializer)?;
  234. Ok(&*Box::leak(deserialized.into_boxed_str()))
  235. }
  236. #[cfg(feature = "serialize")]
  237. fn deserialize_bytes_leaky<'a, 'de, D>(deserializer: D) -> Result<&'a [&'a [u8]], D::Error>
  238. where
  239. D: serde::Deserializer<'de>,
  240. {
  241. use serde::Deserialize;
  242. let deserialized = Vec::<Vec<u8>>::deserialize(deserializer)?;
  243. let deserialized = deserialized
  244. .into_iter()
  245. .map(|v| &*Box::leak(v.into_boxed_slice()))
  246. .collect::<Vec<_>>();
  247. Ok(&*Box::leak(deserialized.into_boxed_slice()))
  248. }
  249. #[cfg(feature = "serialize")]
  250. fn deserialize_leaky<'a, 'de, T: serde::Deserialize<'de>, D>(
  251. deserializer: D,
  252. ) -> Result<&'a [T], D::Error>
  253. where
  254. T: serde::Deserialize<'de>,
  255. D: serde::Deserializer<'de>,
  256. {
  257. use serde::Deserialize;
  258. let deserialized = Box::<[T]>::deserialize(deserializer)?;
  259. Ok(&*Box::leak(deserialized))
  260. }
  261. #[cfg(feature = "serialize")]
  262. fn deserialize_option_leaky<'a, 'de, D>(deserializer: D) -> Result<Option<&'static str>, D::Error>
  263. where
  264. D: serde::Deserializer<'de>,
  265. {
  266. use serde::Deserialize;
  267. let deserialized = Option::<String>::deserialize(deserializer)?;
  268. Ok(deserialized.map(|deserialized| &*Box::leak(deserialized.into_boxed_str())))
  269. }
  270. impl Template {
  271. /// Is this template worth caching at all, since it's completely runtime?
  272. ///
  273. /// There's no point in saving templates that are completely dynamic, since they'll be recreated every time anyway.
  274. pub fn is_completely_dynamic(&self) -> bool {
  275. use TemplateNode::*;
  276. self.roots
  277. .iter()
  278. .all(|root| matches!(root, Dynamic { .. } | DynamicText { .. }))
  279. }
  280. }
  281. /// A statically known node in a layout.
  282. ///
  283. /// This can be created at compile time, saving the VirtualDom time when diffing the tree
  284. #[derive(Debug, Clone, Copy, PartialEq, Hash, Eq, PartialOrd, Ord)]
  285. #[cfg_attr(
  286. feature = "serialize",
  287. derive(serde::Serialize, serde::Deserialize),
  288. serde(tag = "type")
  289. )]
  290. pub enum TemplateNode {
  291. /// An statically known element in the dom.
  292. ///
  293. /// In HTML this would be something like `<div id="123"> </div>`
  294. Element {
  295. /// The name of the element
  296. ///
  297. /// IE for a div, it would be the string "div"
  298. tag: &'static str,
  299. /// The namespace of the element
  300. ///
  301. /// In HTML, this would be a valid URI that defines a namespace for all elements below it
  302. /// SVG is an example of this namespace
  303. #[cfg_attr(
  304. feature = "serialize",
  305. serde(deserialize_with = "deserialize_option_leaky")
  306. )]
  307. namespace: Option<&'static str>,
  308. /// A list of possibly dynamic attributes for this element
  309. ///
  310. /// An attribute on a DOM node, such as `id="my-thing"` or `href="https://example.com"`.
  311. #[cfg_attr(feature = "serialize", serde(deserialize_with = "deserialize_leaky"))]
  312. attrs: &'static [TemplateAttribute],
  313. /// A list of template nodes that define another set of template nodes
  314. #[cfg_attr(feature = "serialize", serde(deserialize_with = "deserialize_leaky"))]
  315. children: &'static [TemplateNode],
  316. },
  317. /// This template node is just a piece of static text
  318. Text {
  319. /// The actual text
  320. text: &'static str,
  321. },
  322. /// This template node is unknown, and needs to be created at runtime.
  323. Dynamic {
  324. /// The index of the dynamic node in the VNode's dynamic_nodes list
  325. id: usize,
  326. },
  327. /// This template node is known to be some text, but needs to be created at runtime
  328. ///
  329. /// This is separate from the pure Dynamic variant for various optimizations
  330. DynamicText {
  331. /// The index of the dynamic node in the VNode's dynamic_nodes list
  332. id: usize,
  333. },
  334. }
  335. impl TemplateNode {
  336. /// Try to load the dynamic node at the given index
  337. pub fn dynamic_id(&self) -> Option<usize> {
  338. use TemplateNode::*;
  339. match self {
  340. Dynamic { id } | DynamicText { id } => Some(*id),
  341. _ => None,
  342. }
  343. }
  344. }
  345. /// A node created at runtime
  346. ///
  347. /// This node's index in the DynamicNode list on VNode should match its repsective `Dynamic` index
  348. #[derive(Debug)]
  349. pub enum DynamicNode {
  350. /// A component node
  351. ///
  352. /// Most of the time, Dioxus will actually know which component this is as compile time, but the props and
  353. /// assigned scope are dynamic.
  354. ///
  355. /// The actual VComponent can be dynamic between two VNodes, though, allowing implementations to swap
  356. /// the render function at runtime
  357. Component(VComponent),
  358. /// A text node
  359. Text(VText),
  360. /// A placeholder
  361. ///
  362. /// Used by suspense when a node isn't ready and by fragments that don't render anything
  363. ///
  364. /// In code, this is just an ElementId whose initial value is set to 0 upon creation
  365. Placeholder(VPlaceholder),
  366. /// A list of VNodes.
  367. ///
  368. /// Note that this is not a list of dynamic nodes. These must be VNodes and created through conditional rendering
  369. /// or iterators.
  370. Fragment(Vec<VNode>),
  371. }
  372. impl DynamicNode {
  373. /// Convert any item that implements [`IntoDynNode`] into a [`DynamicNode`]
  374. pub fn make_node<'c, I>(into: impl IntoDynNode<I> + 'c) -> DynamicNode {
  375. into.into_dyn_node()
  376. }
  377. }
  378. impl Default for DynamicNode {
  379. fn default() -> Self {
  380. Self::Placeholder(Default::default())
  381. }
  382. }
  383. #[derive(Clone)]
  384. /// An instance of a child component
  385. pub struct VComponent {
  386. /// The name of this component
  387. pub name: &'static str,
  388. /// The function pointer of the component, known at compile time
  389. ///
  390. /// It is possible that components get folded at compile time, so these shouldn't be really used as a key
  391. pub(crate) render_fn: TypeId,
  392. pub(crate) props: BoxedAnyProps,
  393. }
  394. impl VComponent {
  395. /// Create a new [`VComponent`] variant
  396. ///
  397. ///
  398. /// The given component can be any of four signatures. Remember that an [`Element`] is really a [`Result<VNode>`].
  399. ///
  400. /// ```rust, ignore
  401. /// // Without explicit props
  402. /// fn(Scope) -> Element;
  403. /// async fn(Scope<'_>) -> Element;
  404. ///
  405. /// // With explicit props
  406. /// fn(Props) -> Element;
  407. /// async fn(Scope<Props<'_>>) -> Element;
  408. /// ```
  409. pub fn new<F: ComponentFunction<P> + 'static, P: 'static>(
  410. component: F,
  411. props: F::Props,
  412. fn_name: &'static str,
  413. ) -> Self
  414. where
  415. // The properties must be valid until the next bump frame
  416. F::Props: Properties + 'static,
  417. {
  418. let render_fn_id = TypeId::of::<F>();
  419. let vcomp = VProps::new(component, F::Props::memoize, props, fn_name);
  420. VComponent {
  421. name: fn_name,
  422. render_fn: render_fn_id,
  423. props: BoxedAnyProps::new(vcomp),
  424. }
  425. }
  426. /// Get the scope this node is mounted to if it's mounted
  427. ///
  428. /// This is useful for rendering nodes outside of the VirtualDom, such as in SSR
  429. ///
  430. /// Returns [`None`] if the node is not mounted
  431. pub fn mounted_scope<'a>(
  432. &self,
  433. dynamic_node_index: usize,
  434. vnode: &VNode,
  435. dom: &'a VirtualDom,
  436. ) -> Option<&'a ScopeState> {
  437. let mount = vnode.mount.get().as_usize()?;
  438. let scope_id = dom.mounts.get(mount)?.mounted_dynamic_nodes[dynamic_node_index];
  439. dom.scopes.get(scope_id)
  440. }
  441. }
  442. impl std::fmt::Debug for VComponent {
  443. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  444. f.debug_struct("VComponent")
  445. .field("name", &self.name)
  446. .finish()
  447. }
  448. }
  449. /// A text node
  450. #[derive(Clone, Debug)]
  451. pub struct VText {
  452. /// The actual text itself
  453. pub value: String,
  454. }
  455. impl VText {
  456. /// Create a new VText
  457. pub fn new(value: String) -> Self {
  458. Self { value }
  459. }
  460. }
  461. impl From<Arguments<'_>> for VText {
  462. fn from(args: Arguments) -> Self {
  463. Self::new(args.to_string())
  464. }
  465. }
  466. /// A placeholder node, used by suspense and fragments
  467. #[derive(Clone, Debug, Default)]
  468. #[non_exhaustive]
  469. pub struct VPlaceholder {}
  470. /// An attribute of the TemplateNode, created at compile time
  471. #[derive(Debug, PartialEq, Hash, Eq, PartialOrd, Ord)]
  472. #[cfg_attr(
  473. feature = "serialize",
  474. derive(serde::Serialize, serde::Deserialize),
  475. serde(tag = "type")
  476. )]
  477. pub enum TemplateAttribute {
  478. /// This attribute is entirely known at compile time, enabling
  479. Static {
  480. /// The name of this attribute.
  481. ///
  482. /// For example, the `href` attribute in `href="https://example.com"`, would have the name "href"
  483. name: &'static str,
  484. /// The value of this attribute, known at compile time
  485. ///
  486. /// Currently this only accepts &str, so values, even if they're known at compile time, are not known
  487. value: &'static str,
  488. /// The namespace of this attribute. Does not exist in the HTML spec
  489. namespace: Option<&'static str>,
  490. },
  491. /// The attribute in this position is actually determined dynamically at runtime
  492. ///
  493. /// This is the index into the dynamic_attributes field on the container VNode
  494. Dynamic {
  495. /// The index
  496. id: usize,
  497. },
  498. }
  499. /// An attribute on a DOM node, such as `id="my-thing"` or `href="https://example.com"`
  500. #[derive(Debug)]
  501. pub struct Attribute {
  502. /// The name of the attribute.
  503. pub name: &'static str,
  504. /// The value of the attribute
  505. pub value: AttributeValue,
  506. /// The namespace of the attribute.
  507. ///
  508. /// Doesn’t exist in the html spec. Used in Dioxus to denote “style” tags and other attribute groups.
  509. pub namespace: Option<&'static str>,
  510. /// An indication of we should always try and set the attribute. Used in controlled components to ensure changes are propagated
  511. pub volatile: bool,
  512. }
  513. impl Attribute {
  514. /// Create a new [`Attribute`] from a name, value, namespace, and volatile bool
  515. ///
  516. /// "Volatile" referes to whether or not Dioxus should always override the value. This helps prevent the UI in
  517. /// some renderers stay in sync with the VirtualDom's understanding of the world
  518. pub fn new(
  519. name: &'static str,
  520. value: impl IntoAttributeValue,
  521. namespace: Option<&'static str>,
  522. volatile: bool,
  523. ) -> Attribute {
  524. Attribute {
  525. name,
  526. namespace,
  527. volatile,
  528. value: value.into_value(),
  529. }
  530. }
  531. }
  532. /// Any of the built-in values that the Dioxus VirtualDom supports as dynamic attributes on elements
  533. ///
  534. /// These are built-in to be faster during the diffing process. To use a custom value, use the [`AttributeValue::Any`]
  535. /// variant.
  536. pub enum AttributeValue {
  537. /// Text attribute
  538. Text(String),
  539. /// A float
  540. Float(f64),
  541. /// Signed integer
  542. Int(i64),
  543. /// Boolean
  544. Bool(bool),
  545. /// A listener, like "onclick"
  546. Listener(ListenerCb),
  547. /// An arbitrary value that implements PartialEq and is static
  548. Any(Box<dyn AnyValue>),
  549. /// A "none" value, resulting in the removal of an attribute from the dom
  550. None,
  551. }
  552. impl AttributeValue {
  553. /// Create a new [`AttributeValue`] with the listener variant from a callback
  554. ///
  555. /// The callback must be confined to the lifetime of the ScopeState
  556. pub fn listener<T: 'static>(mut callback: impl FnMut(Event<T>) + 'static) -> AttributeValue {
  557. AttributeValue::Listener(EventHandler::new(move |event: Event<dyn Any>| {
  558. if let Ok(data) = event.data.downcast::<T>() {
  559. callback(Event {
  560. propagates: event.propagates,
  561. data,
  562. });
  563. }
  564. }))
  565. }
  566. /// Create a new [`AttributeValue`] with a value that implements [`AnyValue`]
  567. pub fn any_value<T: AnyValue>(value: T) -> AttributeValue {
  568. AttributeValue::Any(Box::new(value))
  569. }
  570. }
  571. pub type ListenerCb = EventHandler<Event<dyn Any>>;
  572. impl std::fmt::Debug for AttributeValue {
  573. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  574. match self {
  575. Self::Text(arg0) => f.debug_tuple("Text").field(arg0).finish(),
  576. Self::Float(arg0) => f.debug_tuple("Float").field(arg0).finish(),
  577. Self::Int(arg0) => f.debug_tuple("Int").field(arg0).finish(),
  578. Self::Bool(arg0) => f.debug_tuple("Bool").field(arg0).finish(),
  579. Self::Listener(_) => f.debug_tuple("Listener").finish(),
  580. Self::Any(_) => f.debug_tuple("Any").finish(),
  581. Self::None => write!(f, "None"),
  582. }
  583. }
  584. }
  585. impl PartialEq for AttributeValue {
  586. fn eq(&self, other: &Self) -> bool {
  587. match (self, other) {
  588. (Self::Text(l0), Self::Text(r0)) => l0 == r0,
  589. (Self::Float(l0), Self::Float(r0)) => l0 == r0,
  590. (Self::Int(l0), Self::Int(r0)) => l0 == r0,
  591. (Self::Bool(l0), Self::Bool(r0)) => l0 == r0,
  592. (Self::Listener(_), Self::Listener(_)) => true,
  593. (Self::Any(l0), Self::Any(r0)) => l0.as_ref().any_cmp(r0.as_ref()),
  594. _ => false,
  595. }
  596. }
  597. }
  598. #[doc(hidden)]
  599. pub trait AnyValue: 'static {
  600. fn any_cmp(&self, other: &dyn AnyValue) -> bool;
  601. fn as_any(&self) -> &dyn Any;
  602. fn type_id(&self) -> TypeId {
  603. self.as_any().type_id()
  604. }
  605. }
  606. impl<T: Any + PartialEq + 'static> AnyValue for T {
  607. fn any_cmp(&self, other: &dyn AnyValue) -> bool {
  608. if let Some(other) = other.as_any().downcast_ref() {
  609. self == other
  610. } else {
  611. false
  612. }
  613. }
  614. fn as_any(&self) -> &dyn Any {
  615. self
  616. }
  617. }
  618. /// A trait that allows various items to be converted into a dynamic node for the rsx macro
  619. pub trait IntoDynNode<A = ()> {
  620. /// Consume this item along with a scopestate and produce a DynamicNode
  621. ///
  622. /// You can use the bump alloactor of the scopestate to creat the dynamic node
  623. fn into_dyn_node(self) -> DynamicNode;
  624. }
  625. impl IntoDynNode for () {
  626. fn into_dyn_node(self) -> DynamicNode {
  627. DynamicNode::default()
  628. }
  629. }
  630. impl IntoDynNode for VNode {
  631. fn into_dyn_node(self) -> DynamicNode {
  632. DynamicNode::Fragment(vec![self])
  633. }
  634. }
  635. impl IntoDynNode for DynamicNode {
  636. fn into_dyn_node(self) -> DynamicNode {
  637. self
  638. }
  639. }
  640. impl<T: IntoDynNode> IntoDynNode for Option<T> {
  641. fn into_dyn_node(self) -> DynamicNode {
  642. match self {
  643. Some(val) => val.into_dyn_node(),
  644. None => DynamicNode::default(),
  645. }
  646. }
  647. }
  648. impl IntoDynNode for &Element {
  649. fn into_dyn_node(self) -> DynamicNode {
  650. match self.as_ref() {
  651. Some(val) => val.clone().into_dyn_node(),
  652. _ => DynamicNode::default(),
  653. }
  654. }
  655. }
  656. impl IntoDynNode for &str {
  657. fn into_dyn_node(self) -> DynamicNode {
  658. DynamicNode::Text(VText {
  659. value: self.to_string(),
  660. })
  661. }
  662. }
  663. impl IntoDynNode for String {
  664. fn into_dyn_node(self) -> DynamicNode {
  665. DynamicNode::Text(VText { value: self })
  666. }
  667. }
  668. impl IntoDynNode for Arguments<'_> {
  669. fn into_dyn_node(self) -> DynamicNode {
  670. DynamicNode::Text(VText {
  671. value: self.to_string(),
  672. })
  673. }
  674. }
  675. impl IntoDynNode for &VNode {
  676. fn into_dyn_node(self) -> DynamicNode {
  677. DynamicNode::Fragment(vec![self.clone()])
  678. }
  679. }
  680. pub trait IntoVNode {
  681. fn into_vnode(self) -> VNode;
  682. }
  683. impl IntoVNode for VNode {
  684. fn into_vnode(self) -> VNode {
  685. self
  686. }
  687. }
  688. impl IntoVNode for Element {
  689. fn into_vnode(self) -> VNode {
  690. match self {
  691. Some(val) => val.into_vnode(),
  692. _ => VNode::empty().unwrap(),
  693. }
  694. }
  695. }
  696. // Note that we're using the E as a generic but this is never crafted anyways.
  697. pub struct FromNodeIterator;
  698. impl<T, I> IntoDynNode<FromNodeIterator> for T
  699. where
  700. T: Iterator<Item = I>,
  701. I: IntoVNode,
  702. {
  703. fn into_dyn_node(self) -> DynamicNode {
  704. let children: Vec<_> = self.into_iter().map(|node| node.into_vnode()).collect();
  705. if children.is_empty() {
  706. DynamicNode::default()
  707. } else {
  708. DynamicNode::Fragment(children)
  709. }
  710. }
  711. }
  712. /// A value that can be converted into an attribute value
  713. pub trait IntoAttributeValue {
  714. /// Convert into an attribute value
  715. fn into_value(self) -> AttributeValue;
  716. }
  717. impl IntoAttributeValue for AttributeValue {
  718. fn into_value(self) -> AttributeValue {
  719. self
  720. }
  721. }
  722. impl IntoAttributeValue for &str {
  723. fn into_value(self) -> AttributeValue {
  724. AttributeValue::Text(self.to_string())
  725. }
  726. }
  727. impl IntoAttributeValue for String {
  728. fn into_value(self) -> AttributeValue {
  729. AttributeValue::Text(self)
  730. }
  731. }
  732. impl IntoAttributeValue for f64 {
  733. fn into_value(self) -> AttributeValue {
  734. AttributeValue::Float(self)
  735. }
  736. }
  737. impl IntoAttributeValue for i64 {
  738. fn into_value(self) -> AttributeValue {
  739. AttributeValue::Int(self)
  740. }
  741. }
  742. impl IntoAttributeValue for bool {
  743. fn into_value(self) -> AttributeValue {
  744. AttributeValue::Bool(self)
  745. }
  746. }
  747. impl IntoAttributeValue for Arguments<'_> {
  748. fn into_value(self) -> AttributeValue {
  749. AttributeValue::Text(self.to_string())
  750. }
  751. }
  752. impl IntoAttributeValue for Box<dyn AnyValue> {
  753. fn into_value(self) -> AttributeValue {
  754. AttributeValue::Any(self)
  755. }
  756. }
  757. impl<T: IntoAttributeValue> IntoAttributeValue for Option<T> {
  758. fn into_value(self) -> AttributeValue {
  759. match self {
  760. Some(val) => val.into_value(),
  761. None => AttributeValue::None,
  762. }
  763. }
  764. }
  765. /// A trait for anything that has a dynamic list of attributes
  766. pub trait HasAttributes<'a> {
  767. /// Push an attribute onto the list of attributes
  768. fn push_attribute(
  769. self,
  770. name: &'a str,
  771. ns: Option<&'static str>,
  772. attr: impl IntoAttributeValue,
  773. volatile: bool,
  774. ) -> Self;
  775. }