1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
use std::{any::Any, sync::Arc};
use crate::Element;
use async_trait::async_trait;
/// The component trait is the core of the bloom library.
/// It represents the basic unit of a bloom application.
#[async_trait]
pub trait Component: PartialEq<Self> + Send + Sync {
/// The node type is a representation of the native UI elements of the target renderer.
type Node: From<String>;
/// The error type for the entire application.
type Error;
/// Components are roughly equivalent to React components.
/// The struct itself represents the props of the component.
/// The trait has a render method which contains the component logic.
/// It should return the [Element] type, which is easily generated
/// using the rsx macro from the bloom-rsx crate.
/// Within the render function, hooks can be used such as [bloom_core::use_state] and [bloom_core::use_effect].
/// ```
/// use bloom_core::Component;
///
/// #[derive(PartialEq, Debug)]
/// struct Counter {
/// initial_count: i32
/// }
///
/// #[async_trait]
/// impl Component for Counter {
/// type Node = HtmlNode;
/// type Error = ();
///
/// async fn render(self: Arc<Self>) -> Result<Element<Self::Node, Self::Error>, Self::Error> {
/// let count = use_state(|| self.initial_count);
///
/// rsx!(
/// <div>{count}</div>
/// <button on_click={move |_| count.update(|count| *count + 1)}>Increment</button>
/// )
/// ```
///
/// Components should usually implement a builder pattern for construction using the bloom-rsx macro./// Components are roughly equivalent to React components.
/// The struct itself represents the props of the component.
/// The trait has a render method which contains the component logic.
/// It should return the [Element] type, which is easily generated
/// using the rsx macro from the bloom-rsx crate.
/// Within the render function, hooks can be used such as [use_state] and [use_effect].
/// ```
/// use bloom_core::Component;
///
/// #[derive(PartialEq, Debug)]
/// struct Counter {
/// initial_count: i32
/// }
///
/// #[async_trait]
/// impl Component for Counter {
/// type Node = HtmlNode;
/// type Error = ();
///
/// async fn render(self: Arc<Self>) -> Result<Element<Self::Node, Self::Error>, Self::Error> {
/// let count = use_state(|| self.initial_count);
///
/// rsx!(
/// <div>{count}</div>
/// <button on_click={move |_| count.update(|count| *count + 1)}>Increment</button>
/// )
/// ```
///
/// Components should usually implement a builder pattern for construction using the bloom-rsx macro.
async fn render(self: Arc<Self>) -> Result<Element<Self::Node, Self::Error>, Self::Error>;
}
impl<N, E, C> From<C> for Element<N, E>
where
N: From<String>,
C: Component<Node = N, Error = E> + Sized + 'static,
{
fn from(component: C) -> Self {
Element::Component(Arc::new(component))
}
}
#[derive(PartialEq)]
pub enum ComponentDiff {
NewType,
NewProps,
Equal,
}
#[async_trait]
pub trait AnyComponent {
type Node: From<String>;
type Error;
fn compare(&self, other: &dyn Any) -> ComponentDiff;
fn as_any(&self) -> &dyn Any;
async fn render(self: Arc<Self>) -> Result<Element<Self::Node, Self::Error>, Self::Error>;
}
#[async_trait]
impl<C> AnyComponent for C
where
C: Component + 'static,
Self: Sized,
{
type Node = C::Node;
type Error = C::Error;
fn compare(&self, other: &dyn Any) -> ComponentDiff {
let this = self;
if let Some(other) = other.downcast_ref::<C>() {
if this == other {
return ComponentDiff::Equal;
} else {
return ComponentDiff::NewProps;
}
}
ComponentDiff::NewType
}
fn as_any(&self) -> &dyn Any {
self
}
async fn render(self: Arc<Self>) -> Result<Element<Self::Node, Self::Error>, Self::Error> {
Component::render(self).await
}
}
impl<N, E> PartialEq for &(dyn AnyComponent<Node = N, Error = E> + 'static)
where
N: From<String>,
{
fn eq(&self, other: &Self) -> bool {
self.compare(other.as_any()) == ComponentDiff::Equal
}
}