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}