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!(
136 i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize, f32, f64
137);
138
139impl<T: ViewNode, F: FnMut() -> U + 'static, U: Into<View<T>> + 'static> From<F> for View<T> {
140 fn from(f: F) -> Self {
141 T::create_dynamic_view(f)
142 }
143}
144macro_rules! impl_from_tuple {
146 ($($name:ident),*) => {
147 paste::paste! {
148 impl<U, $($name),*> From<($($name,)*)> for View<U>
149 where
150 $($name: Into<View<U>>),*
151 {
152 fn from(t: ($($name,)*)) -> Self {
153 let ($([<$name:lower>]),*) = t;
154 #[allow(unused_mut)]
155 let mut nodes = SmallVec::new();
156 $(
157 nodes.extend([<$name:lower>].into().nodes);
158 )*
159 View { nodes }
160 }
161 }
162 }
163 };
164}
165
166impl_from_tuple!();
167impl_from_tuple!(A, B);
168impl_from_tuple!(A, B, C);
169impl_from_tuple!(A, B, C, D);
170impl_from_tuple!(A, B, C, D, E);
171impl_from_tuple!(A, B, C, D, E, F);
172impl_from_tuple!(A, B, C, D, E, F, G);
173impl_from_tuple!(A, B, C, D, E, F, G, H);
174impl_from_tuple!(A, B, C, D, E, F, G, H, I);
175impl_from_tuple!(A, B, C, D, E, F, G, H, I, J);
176
177pub trait ViewNode: Into<View<Self>> + Sized + 'static {
184 fn append_child(&mut self, child: Self);
187
188 fn append_view(&mut self, view: View<Self>) {
191 for node in view.nodes {
192 self.append_child(node);
193 }
194 }
195
196 fn create_dynamic_view<U: Into<View<Self>> + 'static>(
204 mut f: impl FnMut() -> U + 'static,
205 ) -> View<Self> {
206 f().into()
207 }
208}