Skip to main content

euv_ui/component/debug/view/
fn.rs

1use super::*;
2
3/// A dev-only component for inspecting reactive state inside the
4/// component tree.
5///
6/// Renders a labelled readout block. The body is the result of
7/// invoking the `value` closure on every render, which means
8/// callers typically embed `Signal::get()` calls inside `value`
9/// to subscribe the rendered vnode to live state changes.
10///
11/// The component emits `data-euv-debug` (on the outer wrapper),
12/// `data-euv-debug-label` (on the label span), and
13/// `data-euv-debug-value` (on the value element) so that
14/// CSS / dev-tools / e2e tests can target each piece without
15/// relying on class names.
16///
17/// # Arguments
18///
19/// - `VirtualNode<EuvDebugProps>` - The props node containing label,
20///   value closure, and `expanded` flag.
21///
22/// # Returns
23///
24/// - `VirtualNode` - A labelled Debug readout element.
25#[component]
26pub fn euv_debug(node: VirtualNode<EuvDebugProps>) -> VirtualNode {
27    let EuvDebugProps {
28        label,
29        value,
30        expanded,
31    }: EuvDebugProps = node.try_get_props().unwrap_or_default();
32    // Render-time value: invoke the closure each render so that
33    // any `Signal::get()` calls inside the closure subscribe the
34    // rendered vnode to those signals. The closure is `Rc<dyn Fn>`,
35    // not `FnMut`, so we cannot mutate captured state — but for a
36    // Debug readout, producing a fresh `String` per render is the
37    // right contract anyway.
38    let rendered: String = match value.as_ref() {
39        Some(formatter) => formatter(),
40        // `value: None` means the caller constructed the
41        // component without a formatter. Render an explicit
42        // placeholder so the dev sees the missing-config issue
43        // immediately (rather than an empty Debug box that
44        // silently misleads them into thinking the value is
45        // empty).
46        None => String::from("<no formatter>"),
47    };
48    if expanded {
49        html! {
50            div {
51                class: c_debug()
52                data-euv-debug: "expanded"
53                span {
54                    class: c_debug_label()
55                    data-euv-debug-label: label
56                    label
57                }
58                pre {
59                    class: c_debug_value()
60                    data-euv-debug-value: "expanded"
61                    rendered
62                }
63            }
64        }
65    } else {
66        html! {
67            div {
68                class: c_debug()
69                data-euv-debug: "inline"
70                span {
71                    class: c_debug_label()
72                    data-euv-debug-label: label
73                    label
74                }
75                code {
76                    class: c_debug_value()
77                    data-euv-debug-value: "inline"
78                    rendered
79                }
80            }
81        }
82    }
83}