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