1use std::fmt;
4
5use smallvec::{smallvec, SmallVec};
6use sycamore_core::Children;
7
8use crate::*;
9
10pub struct View<T = HtmlNode> {
15 pub(crate) nodes: SmallVec<[T; 1]>,
17}
18
19impl<T> View<T> {
20 pub fn new() -> Self {
22 Self {
23 nodes: SmallVec::new(),
24 }
25 }
26
27 pub fn from_node(node: T) -> Self {
29 Self {
30 nodes: smallvec![node],
31 }
32 }
33
34 pub fn from_nodes(nodes: Vec<T>) -> Self {
36 Self {
37 nodes: nodes.into(),
38 }
39 }
40
41 pub fn from_dynamic<U: Into<Self> + 'static>(f: impl FnMut() -> U + 'static) -> Self
44 where
45 T: ViewNode,
46 {
47 T::create_dynamic_view(f)
48 }
49
50 pub fn as_web_sys(&self) -> Vec<web_sys::Node>
52 where
53 T: ViewHtmlNode,
54 {
55 self.nodes
56 .iter()
57 .map(|node| node.as_web_sys().clone())
58 .collect()
59 }
60}
61
62impl<T> Default for View<T> {
63 fn default() -> Self {
64 Self::new()
65 }
66}
67
68impl<T> fmt::Debug for View<T> {
69 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
70 f.debug_struct("View").finish()
71 }
72}
73
74impl<T> From<Children<Self>> for View<T> {
75 fn from(children: Children<Self>) -> Self {
76 children.call()
77 }
78}
79
80impl<T> From<Vec<View<T>>> for View<T> {
81 fn from(nodes: Vec<View<T>>) -> Self {
82 View {
83 nodes: nodes.into_iter().flat_map(|v| v.nodes).collect(),
84 }
85 }
86}
87
88impl<T> From<Option<View<T>>> for View<T> {
89 fn from(node: Option<View<T>>) -> Self {
90 node.unwrap_or_default()
91 }
92}
93
94impl<T: ViewNode, U: Clone + Into<Self>> From<ReadSignal<U>> for View<T> {
95 fn from(signal: ReadSignal<U>) -> Self {
96 (move || signal.get_clone()).into()
97 }
98}
99impl<T: ViewNode, U: Clone + Into<Self>> From<Signal<U>> for View<T> {
100 fn from(signal: Signal<U>) -> Self {
101 (*signal).into()
102 }
103}
104impl<T: ViewNode, U: Clone + Into<Self> + Into<MaybeDyn<U>>> From<MaybeDyn<U>> for View<T> {
105 fn from(value: MaybeDyn<U>) -> Self {
106 (move || value.get_clone()).into()
107 }
108}
109
110macro_rules! impl_view_from {
111 ($($ty:ty),*) => {
112 $(
113 impl<T: ViewHtmlNode> From<$ty> for View<T> {
114 fn from(t: $ty) -> Self {
115 View::from_node(T::create_text_node(t.into()))
116 }
117 }
118 )*
119 }
120}
121
122macro_rules! impl_view_from_to_string {
123 ($($ty:ty),*) => {
124 $(
125 impl<T: ViewHtmlNode> From<$ty> for View<T> {
126 fn from(t: $ty) -> Self {
127 View::from_node(T::create_text_node(t.to_string().into()))
128 }
129 }
130 )*
131 }
132}
133
134impl_view_from!(&'static str, String, Cow<'static, str>);
135impl_view_from_to_string!(i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize, f32, f64);
136
137impl<T: ViewNode, F: FnMut() -> U + 'static, U: Into<View<T>> + 'static> From<F> for View<T> {
138 fn from(f: F) -> Self {
139 T::create_dynamic_view(f)
140 }
141}
142macro_rules! impl_from_tuple {
144 ($($name:ident),*) => {
145 paste::paste! {
146 impl<U, $($name),*> From<($($name,)*)> for View<U>
147 where
148 $($name: Into<View<U>>),*
149 {
150 fn from(t: ($($name,)*)) -> Self {
151 let ($([<$name:lower>]),*) = t;
152 #[allow(unused_mut)]
153 let mut nodes = SmallVec::new();
154 $(
155 nodes.extend([<$name:lower>].into().nodes);
156 )*
157 View { nodes }
158 }
159 }
160 }
161 };
162}
163
164impl_from_tuple!();
165impl_from_tuple!(A, B);
166impl_from_tuple!(A, B, C);
167impl_from_tuple!(A, B, C, D);
168impl_from_tuple!(A, B, C, D, E);
169impl_from_tuple!(A, B, C, D, E, F);
170impl_from_tuple!(A, B, C, D, E, F, G);
171impl_from_tuple!(A, B, C, D, E, F, G, H);
172impl_from_tuple!(A, B, C, D, E, F, G, H, I);
173impl_from_tuple!(A, B, C, D, E, F, G, H, I, J);
174
175pub trait ViewNode: Into<View<Self>> + Sized + 'static {
182 fn append_child(&mut self, child: Self);
185
186 fn append_view(&mut self, view: View<Self>) {
189 for node in view.nodes {
190 self.append_child(node);
191 }
192 }
193
194 fn create_dynamic_view<U: Into<View<Self>> + 'static>(
202 mut f: impl FnMut() -> U + 'static,
203 ) -> View<Self> {
204 f().into()
205 }
206}