sycamore_core/component.rs
1//! Utilities for components and component properties.
2
3use std::fmt;
4
5use sycamore_reactive::*;
6
7/// Runs the given closure inside a new component scope. In other words, this does the following:
8/// * Create a new untracked scope (see [`untrack`]).
9/// * Call the closure `f` passed to this function.
10#[doc(hidden)]
11pub fn component_scope<T>(f: impl FnOnce() -> T) -> T {
12 untrack(f)
13}
14
15/// A trait that is implemented automatically by the `Props` derive macro.
16///
17/// This is used when constructing components in the `view!` macro.
18///
19/// # Example
20/// Deriving an implementation and using the builder to construct an instance of the struct:
21/// ```
22/// # use sycamore::prelude::*;
23/// #[derive(Props)]
24/// struct ButtonProps {
25/// color: String,
26/// disabled: bool,
27/// }
28///
29/// let builder = <ButtonProps as Props>::builder();
30/// let button_props = builder.color("red".to_string()).disabled(false).build();
31/// ```
32pub trait Props {
33 /// The type of the builder. This allows getting the builder type when the name is unknown (e.g.
34 /// in a macro).
35 type Builder;
36 /// Returns the builder for the type.
37 /// The builder should be automatically generated using the `Props` derive macro.
38 fn builder() -> Self::Builder;
39}
40
41/// Make sure that the `Props` trait is implemented for `()` so that components without props can be
42/// thought as accepting props of type `()`.
43impl Props for () {
44 type Builder = UnitBuilder;
45 fn builder() -> Self::Builder {
46 UnitBuilder
47 }
48}
49
50#[doc(hidden)]
51#[derive(Debug)]
52pub struct UnitBuilder;
53
54impl UnitBuilder {
55 pub fn build(self) {}
56}
57
58/// A trait that is automatically implemented by all components.
59pub trait Component<T: Props, V, S> {
60 /// Instantiate the component with the given props and reactive scope.
61 fn create(self, props: T) -> V;
62}
63impl<F, T: Props, V> Component<T, V, ((),)> for F
64where
65 F: FnOnce(T) -> V,
66{
67 fn create(self, props: T) -> V {
68 self(props)
69 }
70}
71impl<F, V> Component<(), V, ()> for F
72where
73 F: FnOnce() -> V,
74{
75 fn create(self, _props: ()) -> V {
76 self()
77 }
78}
79
80/// Get the builder for the component function.
81#[doc(hidden)]
82pub fn element_like_component_builder<T: Props, V, S>(_f: &impl Component<T, V, S>) -> T::Builder {
83 T::builder()
84}
85
86/// A special property type to allow the component to accept children.
87///
88/// Add a field called `children` of this type to your properties struct.
89///
90/// # Example
91/// ```
92/// # use sycamore::prelude::*;
93/// #[derive(Props)]
94/// struct RowProps {
95/// width: i32,
96/// children: Children,
97/// }
98///
99/// #[component]
100/// fn Row(props: RowProps) -> View {
101/// // Convert the `Children` into a `View`.
102/// let children = props.children.call();
103/// view! {
104/// div {
105/// (children)
106/// }
107/// }
108/// }
109///
110/// # #[component]
111/// # fn App() -> View {
112/// // Using `Row` somewhere else in your app:
113/// view! {
114/// Row(width=10) {
115/// p { "This is a child node." }
116/// }
117/// }
118/// # }
119/// ```
120pub struct Children<V> {
121 f: Box<dyn FnOnce() -> V>,
122}
123impl<V> fmt::Debug for Children<V> {
124 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
125 f.debug_struct("Children").finish()
126 }
127}
128
129impl<F, V> From<F> for Children<V>
130where
131 F: FnOnce() -> V + 'static,
132{
133 fn from(f: F) -> Self {
134 Self { f: Box::new(f) }
135 }
136}
137
138impl<V: Default + 'static> Default for Children<V> {
139 fn default() -> Self {
140 Self {
141 f: Box::new(V::default),
142 }
143 }
144}
145
146impl<V> Children<V> {
147 /// Instantiates the child view.
148 pub fn call(self) -> V {
149 (self.f)()
150 }
151
152 /// Create a new [`Children`] from a closure.
153 pub fn new(f: impl FnOnce() -> V + 'static) -> Self {
154 Self { f: Box::new(f) }
155 }
156}