pax_runtime/
conditional.rs

1use std::{iter, rc::Rc};
2use_RefCell!();
3
4use pax_runtime_api::pax_value::ImplToFromPaxAny;
5use pax_runtime_api::{borrow, borrow_mut, use_RefCell, PaxValue, Property, ToPaxValue};
6
7use crate::api::Layer;
8use crate::{
9    BaseInstance, ExpandedNode, InstanceFlags, InstanceNode, InstantiationArgs, RuntimeContext,
10};
11
12/// A special "control-flow" primitive, Conditional (`if`) allows for a
13/// subtree of a component template to be rendered conditionally,
14/// based on the value of the property `boolean_expression`.
15/// The Pax compiler handles ConditionalInstance specially
16/// with the `if` syntax in templates.
17pub struct ConditionalInstance {
18    base: BaseInstance,
19}
20
21impl ImplToFromPaxAny for ConditionalProperties {}
22
23///Contains the expression of a conditional, evaluated as an expression.
24#[derive(Default)]
25pub struct ConditionalProperties {
26    pub boolean_expression: Property<bool>,
27}
28
29impl ToPaxValue for ConditionalProperties {
30    fn to_pax_value(self) -> PaxValue {
31        PaxValue::Object(
32            vec![(
33                "boolean_expression".to_string(),
34                self.boolean_expression.to_pax_value(),
35            )]
36            .into_iter()
37            .collect(),
38        )
39    }
40}
41
42impl InstanceNode for ConditionalInstance {
43    fn instantiate(args: InstantiationArgs) -> Rc<Self>
44    where
45        Self: Sized,
46    {
47        Rc::new(Self {
48            base: BaseInstance::new(
49                args,
50                InstanceFlags {
51                    invisible_to_slot: true,
52                    invisible_to_raycasting: true,
53                    layer: Layer::DontCare,
54                    is_component: false,
55                    is_slot: false,
56                },
57            ),
58        })
59    }
60
61    fn handle_mount(
62        self: Rc<Self>,
63        expanded_node: &Rc<ExpandedNode>,
64        context: &Rc<RuntimeContext>,
65    ) {
66        self.handle_setup(expanded_node, context, true);
67    }
68
69    fn handle_control_flow_node_expansion(
70        self: Rc<Self>,
71        expanded_node: &Rc<ExpandedNode>,
72        context: &Rc<RuntimeContext>,
73    ) {
74        self.handle_setup(expanded_node, context, false);
75    }
76
77    fn resolve_debug(
78        &self,
79        f: &mut std::fmt::Formatter,
80        _expanded_node: Option<&ExpandedNode>,
81    ) -> std::fmt::Result {
82        f.debug_struct("Conditional").finish()
83    }
84
85    fn base(&self) -> &BaseInstance {
86        &self.base
87    }
88}
89
90impl ConditionalInstance {
91    fn handle_setup(
92        self: Rc<Self>,
93        expanded_node: &Rc<ExpandedNode>,
94        context: &Rc<RuntimeContext>,
95        is_mount: bool,
96    ) {
97        let weak_ref_self = Rc::downgrade(expanded_node);
98        let cloned_self = Rc::clone(&self);
99        let cloned_context = Rc::clone(context);
100
101        let cond_expr =
102            expanded_node.with_properties_unwrapped(|properties: &mut ConditionalProperties| {
103                properties.boolean_expression.clone()
104            });
105
106        let dep = cond_expr.untyped();
107
108        let old_val = RefCell::new(false);
109        expanded_node
110            .children
111            .replace_with(Property::computed_with_name(
112                move || {
113                    let Some(cloned_expanded_node) = weak_ref_self.upgrade() else {
114                        panic!("ran evaluator after expanded node dropped (conditional elem)")
115                    };
116                    let val = cond_expr.get();
117                    if val == *borrow!(old_val) {
118                        return cloned_expanded_node.children.get();
119                    }
120                    *borrow_mut!(old_val) = val;
121                    if val {
122                        let env = Rc::clone(&cloned_expanded_node.stack);
123                        let children = borrow!(cloned_self.base().get_instance_children());
124                        let children_with_envs = children.iter().cloned().zip(iter::repeat(env));
125                        let res = cloned_expanded_node.generate_children(
126                            children_with_envs,
127                            &cloned_context,
128                            &cloned_expanded_node.parent_frame,
129                            is_mount,
130                        );
131                        res
132                    } else {
133                        cloned_expanded_node.generate_children(
134                            vec![],
135                            &cloned_context,
136                            &cloned_expanded_node.parent_frame,
137                            is_mount,
138                        )
139                    }
140                },
141                &[dep],
142                &format!("conditional_children (node id: {})", expanded_node.id.0),
143            ));
144    }
145}