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(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}