Skip to main content

euv_core/vdom/cast/
impl.rs

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