Skip to main content

eye_declare/
children.rs

1use crate::component::Component;
2use crate::element::{ElementHandle, Elements};
3use crate::node::WidthConstraint;
4
5// ---------------------------------------------------------------------------
6// AddTo — how a value adds itself to a collector
7// ---------------------------------------------------------------------------
8
9/// Trait for adding a value to a child collector.
10///
11/// The `element!` macro dispatches all child additions through this trait,
12/// enabling **compile-time type checking** of parent-child relationships.
13/// If you try to nest a component inside a parent that doesn't accept it,
14/// you'll get a compile error rather than a runtime panic.
15///
16/// # Implementations
17///
18/// - **Blanket impl**: any [`Component`] can be added to [`Elements`].
19/// - **Data types**: [`Span`](crate::Span) converts `Into<TextChild>`,
20///   `String` converts `Into<TextChild>`. These produce compile errors
21///   if used in the wrong context.
22pub trait AddTo<Collector: ?Sized> {
23    /// Handle returned after adding. Supports `.key()` / `.width()` chaining.
24    type Handle<'a>
25    where
26        Collector: 'a;
27
28    /// Add this value to the collector, returning a handle for chaining
29    /// `.key()` and `.width()`.
30    fn add_to(self, collector: &mut Collector) -> Self::Handle<'_>;
31}
32
33/// Blanket: any Component can be added to Elements.
34impl<C: Component> AddTo<Elements> for C {
35    type Handle<'a> = ElementHandle<'a>;
36
37    fn add_to(self, els: &mut Elements) -> ElementHandle<'_> {
38        els.add(self)
39    }
40}
41
42/// String → Elements: creates a Text component with the string as content.
43///
44/// This powers the `element!` string literal sugar — `"hello"` becomes a
45/// [`Text`](crate::Text) component. The same `AddTo` dispatch also works
46/// inside data children contexts (e.g., `Text { "hello" }`) via the
47/// `Into<TextChild>` blanket impl.
48impl AddTo<Elements> for String {
49    type Handle<'a> = ElementHandle<'a>;
50
51    fn add_to(self, els: &mut Elements) -> ElementHandle<'_> {
52        let text = crate::components::text::Text::unstyled(self);
53        els.add(text)
54    }
55}
56
57// ---------------------------------------------------------------------------
58// SpliceInto — how Elements splice into a collector
59// ---------------------------------------------------------------------------
60
61/// Trait for splicing an [`Elements`] list inline into a collector.
62///
63/// Used by the `element!` macro's `#(expr)` syntax. Only implemented
64/// for `Elements` → `Elements` — using `#(expr)` inside a data
65/// collector (e.g., inside a `Line { }` block) produces a compile error.
66pub trait SpliceInto<Collector: ?Sized> {
67    /// Splice all entries from this value into the collector.
68    fn splice_into(self, collector: &mut Collector);
69}
70
71impl SpliceInto<Elements> for Elements {
72    fn splice_into(self, collector: &mut Elements) {
73        collector.splice(self);
74    }
75}
76
77// ---------------------------------------------------------------------------
78// ChildCollector — how a component collects and finalizes children
79// ---------------------------------------------------------------------------
80
81/// Declares how a component collects children in the `element!` macro.
82///
83/// Implement this trait to allow your component to accept children in
84/// `element!` braces. The `Collector` type determines which child types
85/// are valid (via [`AddTo`]).
86///
87/// # Two patterns
88///
89/// - **Slot children** (layout containers like [`VStack`](crate::VStack)):
90///   Use `Elements` as `Collector` and [`ComponentWithSlot`] as `Output`.
91///   The [`impl_slot_children!`](crate::impl_slot_children) macro does this
92///   automatically.
93///
94/// - **Data children** (like [`Text`](crate::Text)):
95///   Use a custom collector type. For `#[component]` functions, the macro
96///   generates a wrapper that holds the collected data.
97///
98/// Components without `ChildCollector` produce a compile error when used
99/// with children in `element!`.
100pub trait ChildCollector: Sized {
101    /// The type used to accumulate children.
102    type Collector: Default;
103
104    /// The output type after finalizing children.
105    ///
106    /// For layout containers: [`ComponentWithSlot<Self>`].
107    /// For data components: `Self` (with data absorbed).
108    type Output;
109
110    /// Finalize children collection, producing the output value.
111    fn finish(self, collector: Self::Collector) -> Self::Output;
112}
113
114// ---------------------------------------------------------------------------
115// ComponentWithSlot — wrapper for component + slot children
116// ---------------------------------------------------------------------------
117
118/// Wrapper pairing a component with its slot children.
119///
120/// Produced by [`ChildCollector::finish`] for layout containers.
121/// The `element!` macro creates this automatically when you write
122/// `Component { children... }` for a component that uses
123/// [`impl_slot_children!`](crate::impl_slot_children).
124pub struct ComponentWithSlot<C> {
125    component: C,
126    children: Elements,
127}
128
129impl<C> ComponentWithSlot<C> {
130    /// Create a new component-with-slot wrapper.
131    pub fn new(component: C, children: Elements) -> Self {
132        Self {
133            component,
134            children,
135        }
136    }
137}
138
139impl<C: Component> AddTo<Elements> for ComponentWithSlot<C> {
140    type Handle<'a> = ElementHandle<'a>;
141
142    fn add_to(self, els: &mut Elements) -> ElementHandle<'_> {
143        els.add_with_children(self.component, self.children)
144    }
145}
146
147// ---------------------------------------------------------------------------
148// DataChildren<T> — generic collector for typed data children via Into<T>
149// ---------------------------------------------------------------------------
150
151/// Generic collector for components that accept typed data children.
152///
153/// `DataChildren<T>` collects children via `Into<T>` conversions, where `T`
154/// is a child enum defined by the component. This replaces per-component
155/// collector types with a single generic pattern.
156///
157/// # Example
158///
159/// ```ignore
160/// // Component defines what children it accepts
161/// enum TextChild {
162///     Span(Span),
163/// }
164///
165/// impl From<Span> for TextChild {
166///     fn from(s: Span) -> Self { TextChild::Span(s) }
167/// }
168///
169/// // #[component] generates the ChildCollector automatically:
170/// // #[component(props = MyText, children = DataChildren<TextChild>)]
171/// // fn my_text(props: &MyText, children: &DataChildren<TextChild>) -> Elements { ... }
172/// ```
173pub struct DataChildren<T>(Vec<T>);
174
175impl<T> DataChildren<T> {
176    /// Consume the collector and return the collected children.
177    pub fn into_vec(self) -> Vec<T> {
178        self.0
179    }
180
181    /// Borrow the collected children as a slice.
182    pub fn as_slice(&self) -> &[T] {
183        &self.0
184    }
185}
186
187impl<T> Default for DataChildren<T> {
188    fn default() -> Self {
189        DataChildren(Vec::new())
190    }
191}
192
193/// Any type that converts `Into<T>` can be added to a `DataChildren<T>` collector.
194impl<T, V: Into<T>> AddTo<DataChildren<T>> for V {
195    type Handle<'a>
196        = DataHandle
197    where
198        T: 'a;
199
200    fn add_to(self, collector: &mut DataChildren<T>) -> DataHandle {
201        collector.0.push(self.into());
202        DataHandle
203    }
204}
205
206// ---------------------------------------------------------------------------
207// DataHandle — no-op handle for data child additions
208// ---------------------------------------------------------------------------
209
210/// No-op handle returned when adding data children (e.g., [`Span`](crate::Span))
211/// to a custom collector.
212///
213/// Provides `.key()` and `.width()` methods that silently do nothing,
214/// so the `element!` macro's chaining syntax compiles in all contexts.
215/// Keys and width constraints are only meaningful on [`Elements`] entries.
216pub struct DataHandle;
217
218impl DataHandle {
219    /// No-op — keys are only meaningful on [`Elements`] entries.
220    pub fn key(self, _key: impl Into<String>) -> Self {
221        self
222    }
223
224    /// No-op — width constraints are only meaningful on [`Elements`] entries.
225    pub fn width(self, _constraint: WidthConstraint) -> Self {
226        self
227    }
228}