Skip to main content

euv_core/vdom/cast/
impl.rs

1use crate::*;
2
3/// Converts a `VirtualNode` into itself via `IntoNode`.
4impl IntoNode for VirtualNode {
5    /// Returns this virtual node as-is.
6    ///
7    /// # Returns
8    ///
9    /// - `VirtualNode` - This same virtual node.
10    fn into_node(self) -> VirtualNode {
11        self
12    }
13}
14
15/// Converts a `Vec<VirtualNode>` into a `VirtualNode::Fragment` via `IntoNode`.
16///
17/// This enables using a `Vec<VirtualNode>` directly in the `html!` macro
18/// without manually wrapping it in `VirtualNode::Fragment(...)`.
19///
20/// # Returns
21///
22/// - `VirtualNode` - A `VirtualNode::Fragment` containing the nodes, or
23///   `VirtualNode::Empty` if the vector is empty.
24impl IntoNode for Vec<VirtualNode> {
25    fn into_node(self) -> VirtualNode {
26        if self.is_empty() {
27            VirtualNode::Empty
28        } else {
29            VirtualNode::Fragment(self)
30        }
31    }
32}
33
34/// Converts an `Option<VirtualNode>` into a `VirtualNode` via `IntoNode`.
35///
36/// `Some(node)` returns the inner node, `None` returns `VirtualNode::Empty`.
37///
38/// # Returns
39///
40/// - `VirtualNode` - The inner node if `Some`, otherwise `VirtualNode::Empty`.
41impl IntoNode for Option<VirtualNode> {
42    fn into_node(self) -> VirtualNode {
43        match self {
44            Some(node) => node,
45            None => VirtualNode::Empty,
46        }
47    }
48}
49
50/// Converts an `Option<Vec<VirtualNode>>` into a `VirtualNode` via `IntoNode`.
51///
52/// `Some(vec)` converts the vector into a `VirtualNode::Fragment` (or `Empty`
53/// if the vector is empty), `None` returns `VirtualNode::Empty`.
54///
55/// # Returns
56///
57/// - `VirtualNode` - A `VirtualNode::Fragment` if `Some` with nodes,
58///   `VirtualNode::Empty` if `None` or the vector is empty.
59impl IntoNode for Option<Vec<VirtualNode>> {
60    fn into_node(self) -> VirtualNode {
61        match self {
62            Some(nodes) => nodes.into_node(),
63            None => VirtualNode::Empty,
64        }
65    }
66}
67
68/// Wraps a `FnMut() -> VirtualNode` closure into a `DynamicNode` via `IntoNode`.
69///
70/// This enables writing `{move || html! { ... }}` directly in HTML markup
71/// without explicit `DynamicNode` construction.
72impl<F> IntoNode for F
73where
74    F: FnMut() -> VirtualNode + 'static,
75{
76    /// Wraps this closure into a `VirtualNode::Dynamic` with a fresh hook context.
77    ///
78    /// # Returns
79    ///
80    /// - `VirtualNode` - A dynamic virtual node wrapping this closure.
81    fn into_node(self) -> VirtualNode {
82        let render_inner: Rc<RefCell<RenderFnInner>> =
83            Rc::new(RefCell::new(RenderFnInner::new(Box::new(self))));
84        VirtualNode::Dynamic(DynamicNode::new(
85            render_inner,
86            crate::reactive::create_hook_context(),
87        ))
88    }
89}
90
91/// Converts a `String` into a text virtual node via `IntoNode`.
92impl IntoNode for String {
93    /// Converts this string into a text virtual node.
94    ///
95    /// # Returns
96    ///
97    /// - `VirtualNode` - A text virtual node.
98    fn into_node(self) -> VirtualNode {
99        VirtualNode::Text(TextNode::new(self, None))
100    }
101}
102
103/// Converts a `&str` into a text virtual node via `IntoNode`.
104impl IntoNode for &str {
105    /// Converts this string slice into a text virtual node.
106    ///
107    /// # Returns
108    ///
109    /// - `VirtualNode` - A text virtual node.
110    fn into_node(self) -> VirtualNode {
111        VirtualNode::Text(TextNode::new(self.to_string(), None))
112    }
113}
114
115/// Converts an `i32` into a text virtual node via `IntoNode`.
116impl IntoNode for i32 {
117    /// Converts this integer into a text virtual node.
118    ///
119    /// # Returns
120    ///
121    /// - `VirtualNode` - A text virtual node.
122    fn into_node(self) -> VirtualNode {
123        VirtualNode::Text(TextNode::new(self.to_string(), None))
124    }
125}
126
127/// Converts a `usize` into a text virtual node via `IntoNode`.
128impl IntoNode for usize {
129    /// Converts this unsigned integer into a text virtual node.
130    ///
131    /// # Returns
132    ///
133    /// - `VirtualNode` - A text virtual node.
134    fn into_node(self) -> VirtualNode {
135        VirtualNode::Text(TextNode::new(self.to_string(), None))
136    }
137}
138
139/// Converts a `bool` into a text virtual node via `IntoNode`.
140impl IntoNode for bool {
141    /// Converts this boolean into a text virtual node.
142    ///
143    /// # Returns
144    ///
145    /// - `VirtualNode` - A text virtual node.
146    fn into_node(self) -> VirtualNode {
147        VirtualNode::Text(TextNode::new(self.to_string(), None))
148    }
149}
150
151/// Converts a signal into a reactive text virtual node via `IntoNode`.
152impl<T> IntoNode for Signal<T>
153where
154    T: Clone + PartialEq + Display + 'static,
155{
156    /// Converts this signal into a reactive text virtual node.
157    ///
158    /// # Returns
159    ///
160    /// - `VirtualNode` - A reactive text virtual node.
161    fn into_node(self) -> VirtualNode {
162        self.as_reactive_text()
163    }
164}
165
166/// Converts a signal into a reactive text node with listener wiring.
167impl<T> AsReactiveText for Signal<T>
168where
169    T: Clone + PartialEq + Display + 'static,
170{
171    /// Creates a reactive text node that auto-updates when the signal changes.
172    ///
173    /// Internally creates a bridge `Signal<String>` that subscribes to the
174    /// source signal and updates the text content on every change.
175    ///
176    /// # Returns
177    ///
178    /// - `VirtualNode` - A text virtual node with reactive signal binding.
179    fn as_reactive_text(&self) -> VirtualNode {
180        let initial: String = self.get().to_string();
181        let string_signal: Signal<String> = Signal::create(initial.clone());
182        let source_signal: Signal<T> = *self;
183        let string_signal_clone: Signal<String> = string_signal;
184        source_signal.replace_subscribe({
185            let source_inner: Signal<T> = source_signal;
186            move || {
187                let new_value: String = source_inner.get().to_string();
188                string_signal_clone.set_silent(new_value);
189            }
190        });
191        VirtualNode::Text(TextNode::new(initial, Some(string_signal)))
192    }
193}
194
195/// Constructs an `EventAdapter` that wraps any event-compatible value.
196impl<T> EventAdapter<T> {
197    /// Returns the inner wrapped value, consuming the adapter.
198    ///
199    /// # Returns
200    ///
201    /// - `T` - The inner value.
202    pub(crate) fn into_inner(self) -> T {
203        self.inner
204    }
205}
206
207/// Adapts a `FnMut(Event)` closure into an `AttributeValue::Event`.
208///
209/// Wraps the closure into a `NativeEventHandler` and returns it as an
210/// event attribute value. This replaces the `__EventWrapper<F>` type
211/// that was previously generated inline by the `html!` macro.
212impl<F> EventAdapter<F>
213where
214    F: FnMut(Event) + 'static,
215{
216    /// Converts the wrapped closure into an event `AttributeValue`.
217    ///
218    /// # Arguments
219    ///
220    /// - `NativeEventName` - The event name enum variant to associate with the handler.
221    ///
222    /// # Returns
223    ///
224    /// - `AttributeValue` - An `AttributeValue::Event` wrapping the handler.
225    pub fn into_attribute(self, event_name: NativeEventName) -> AttributeValue {
226        AttributeValue::Event(NativeEventHandler::create(event_name, self.into_inner()))
227    }
228}
229
230/// Adapts an owned `NativeEventHandler` into an `AttributeValue::Event` directly.
231///
232/// When the user already provides a `NativeEventHandler`, the handler is
233/// re-wrapped with the given `event_name` to ensure the DOM event listener
234/// is bound to the correct event type (e.g., "click" rather than "onclick").
235/// This replaces the `impl __EventWrapper<NativeEventHandler>` that was
236/// previously generated inline.
237impl EventAdapter<NativeEventHandler> {
238    /// Converts the wrapped handler into an event `AttributeValue`.
239    ///
240    /// Re-wraps the handler with the provided `event_name` so that the
241    /// DOM event listener uses the correct event type string.
242    ///
243    /// # Arguments
244    ///
245    /// - `NativeEventName` - The event name to bind the handler to.
246    ///
247    /// # Returns
248    ///
249    /// - `AttributeValue` - An `AttributeValue::Event` containing the re-wrapped handler.
250    pub fn into_attribute(self, event_name: NativeEventName) -> AttributeValue {
251        let mut handler: NativeEventHandler = self.into_inner();
252        handler.set_event_name(event_name.to_string());
253        AttributeValue::Event(handler)
254    }
255}
256
257/// Adapts an `Option<NativeEventHandler>` into an `AttributeValue`.
258///
259/// `Some(handler)` becomes `AttributeValue::Event(handler)` re-wrapped with the
260/// given event name, and `None` becomes `AttributeValue::Text(String::new())`.
261/// This replaces the `impl __EventWrapper<Option<NativeEventHandler>>` that was
262/// previously generated inline by the `html!` macro.
263impl EventAdapter<Option<NativeEventHandler>> {
264    /// Converts the wrapped optional handler into an attribute value.
265    ///
266    /// Re-wraps a `Some` handler with the provided `event_name` so that the
267    /// DOM event listener uses the correct event type string.
268    ///
269    /// # Arguments
270    ///
271    /// - `NativeEventName` - The event name to bind the handler to.
272    ///
273    /// # Returns
274    ///
275    /// - `AttributeValue` - An event attribute if `Some`, otherwise an empty text attribute.
276    pub fn into_attribute(self, event_name: NativeEventName) -> AttributeValue {
277        match self.into_inner() {
278            Some(handler) => EventAdapter::new(handler).into_attribute(event_name),
279            None => AttributeValue::Text(String::new()),
280        }
281    }
282}
283
284/// Constructs an `AttrValueAdapter` that wraps any attribute-compatible value.
285impl<T> AttrValueAdapter<T> {
286    /// Returns the inner wrapped value, consuming the adapter.
287    ///
288    /// # Returns
289    ///
290    /// - `T` - The inner value.
291    pub(crate) fn into_inner(self) -> T {
292        self.inner
293    }
294}
295
296/// Adapts a `FnMut(Event)` closure into a callback `AttributeValue`.
297///
298/// This handles the case where a closure is used as a component callback prop.
299/// The closure is converted via `IntoCallbackAttribute::into_callback_attribute()`.
300/// This replaces the `__IsClosure for F` impl that was previously generated inline.
301impl<F> AttrValueAdapter<F>
302where
303    F: FnMut(Event) + 'static,
304{
305    /// Converts the wrapped closure into a callback `AttributeValue`.
306    ///
307    /// # Returns
308    ///
309    /// - `AttributeValue` - An event attribute value wrapping the adapted closure.
310    pub fn into_callback_attribute_value(self) -> AttributeValue {
311        self.into_inner().into_callback_attribute()
312    }
313
314    /// Converts the wrapped closure into a callback `AttributeValue` with a
315    /// custom event name for component props.
316    ///
317    /// # Arguments
318    ///
319    /// - `String` - The custom attribute name (e.g., "on-increment", "on-change").
320    ///
321    /// # Returns
322    ///
323    /// - `AttributeValue` - An event attribute value with the custom name.
324    pub fn into_callback_attribute_value_with_name(self, name: String) -> AttributeValue {
325        AttributeValue::Event(NativeEventHandler::create(
326            NativeEventName::Other(name),
327            self.into_inner(),
328        ))
329    }
330}
331
332/// Adapts an owned `NativeEventHandler` into an `AttributeValue::Event` directly.
333///
334/// When the user already provides a `NativeEventHandler`, the handler is
335/// re-wrapped with a generic "callback" event name so that subsequent
336/// `EventAdapter::into_attribute` calls can override it with the correct
337/// DOM event type. This replaces the `__IsClosure for NativeEventHandler`
338/// impl that was previously generated inline by the `html!` macro.
339impl AttrValueAdapter<NativeEventHandler> {
340    /// Converts the wrapped handler into an event `AttributeValue`.
341    ///
342    /// Re-wraps the handler with a generic callback event name so that the
343    /// DOM event type can be correctly resolved when the handler is later
344    /// bound to a real element via `EventAdapter::into_attribute`.
345    ///
346    /// # Returns
347    ///
348    /// - `AttributeValue` - An `AttributeValue::Event` containing the re-wrapped handler.
349    pub fn into_callback_attribute_value(self) -> AttributeValue {
350        AttributeValue::Event(self.into_inner())
351    }
352
353    /// Converts the wrapped handler into a callback `AttributeValue` with a
354    /// custom event name for component props.
355    ///
356    /// Re-wraps the handler with the provided custom event name so that
357    /// `try_get_callback` can find it by the matching attribute name.
358    ///
359    /// # Arguments
360    ///
361    /// - `String` - The custom attribute name.
362    ///
363    /// # Returns
364    ///
365    /// - `AttributeValue` - An event attribute value with the custom name.
366    pub fn into_callback_attribute_value_with_name(self, name: String) -> AttributeValue {
367        let mut handler: NativeEventHandler = self.into_inner();
368        handler.set_event_name(name);
369        AttributeValue::Event(handler)
370    }
371}
372
373/// Adapts an `Option<NativeEventHandler>` into an `AttributeValue`.
374///
375/// `Some(handler)` becomes `AttributeValue::Event(handler)` re-wrapped with
376/// a generic callback event name, and `None` becomes `AttributeValue::Text(String::new())`.
377/// This replaces the `__IsClosure for Option<NativeEventHandler>` impl that was
378/// previously generated inline by the `html!` macro.
379impl AttrValueAdapter<Option<NativeEventHandler>> {
380    /// Converts the wrapped optional handler into an attribute value.
381    ///
382    /// Re-wraps a `Some` handler with a generic callback event name so that the
383    /// DOM event type can be correctly resolved when the handler is later bound
384    /// to a real element via `EventAdapter::into_attribute`.
385    ///
386    /// # Returns
387    ///
388    /// - `AttributeValue` - An event attribute if `Some`, otherwise an empty text attribute.
389    pub fn into_callback_attribute_value(self) -> AttributeValue {
390        match self.into_inner() {
391            Some(handler) => AttrValueAdapter::new(handler).into_callback_attribute_value(),
392            None => AttributeValue::Text(String::new()),
393        }
394    }
395
396    /// Converts this optional handler into a callback `AttributeValue` with a
397    /// custom event name for component props.
398    ///
399    /// # Arguments
400    ///
401    /// - `String` - The custom attribute name.
402    ///
403    /// # Returns
404    ///
405    /// - `AttributeValue` - An event attribute with the custom name if `Some`,
406    ///   otherwise an empty text attribute.
407    pub fn into_callback_attribute_value_with_name(self, name: String) -> AttributeValue {
408        match self.into_inner() {
409            Some(handler) => {
410                AttrValueAdapter::new(handler).into_callback_attribute_value_with_name(name)
411            }
412            None => AttributeValue::Text(String::new()),
413        }
414    }
415}
416
417/// Adapts any `IntoReactiveValue` type into an `AttributeValue`.
418///
419/// This is the fallback path for non-closure attribute values (strings, signals,
420/// CSS classes, etc.). The value is converted via `IntoReactiveValue::into_reactive_value()`.
421/// This replaces the `__ValuePicker` / `__FallbackHelper` hierarchy that was previously
422/// generated inline by the `html!` macro.
423impl<T> AttrValueAdapter<T>
424where
425    T: IntoReactiveValue,
426{
427    /// Converts the wrapped value into an `AttributeValue` via reactive value adaptation.
428    ///
429    /// # Returns
430    ///
431    /// - `AttributeValue` - The reactive attribute value.
432    pub fn into_reactive_attribute_value(self) -> AttributeValue {
433        self.into_inner().into_reactive_value()
434    }
435}