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}