Skip to main content

hypercmd/
bindings.rs

1use crate::{Error, Event, EventResult, Node, Scope, scene::Listener};
2use fusor::bind::{Checkbox, TextValue};
3use std::{cell::RefCell, fmt::Display, rc::Rc};
4
5impl Scope {
6    fn effect(&mut self, node: Node, mut update: impl FnMut(&Node) + 'static) -> Result<(), Error> {
7        self.watch(move || {
8            update(&node);
9            Ok(())
10        })
11    }
12    pub fn text<T: Display>(
13        &mut self,
14        id: usize,
15        read: impl Fn() -> T + 'static,
16    ) -> Result<(), Error> {
17        self.effect(
18            crate::scope::anchor(&self.texts, "text", id)?,
19            move |node| node.set_text(read().to_string()),
20        )
21    }
22    pub fn on<R: EventResult>(
23        &mut self,
24        id: usize,
25        name: &'static str,
26        mut callback: impl FnMut(Event) -> R + 'static,
27    ) -> Result<(), Error> {
28        let node = self.element(id)?;
29        let listener: Listener = Rc::new(RefCell::new(move |event| callback(event).into_result()));
30        node.0
31            .listeners
32            .borrow_mut()
33            .entry(name)
34            .or_default()
35            .push(listener);
36        Ok(())
37    }
38    pub fn bind_text(&mut self, id: usize, model: impl TextValue) -> Result<(), Error> {
39        let input_model = model.clone();
40        self.on(id, "input", move |event| {
41            input_model.edit(event.target.value())
42        })?;
43        let blur_model = model.clone();
44        self.on(id, "blur", move |_| blur_model.touch())?;
45        let node = self.element(id)?;
46        self.watch(move || {
47            if !model.shows(&node.value()) {
48                node.set_value(model.text());
49            }
50            Ok(())
51        })
52    }
53    pub fn bind_checkbox(
54        &mut self,
55        id: usize,
56        model: impl Checkbox,
57        choice: impl Fn() -> String + 'static,
58    ) -> Result<(), Error> {
59        let choice = Rc::new(choice);
60        let (input_choice, input_model) = (choice.clone(), model.clone());
61        self.on(id, "change", move |event| {
62            input_model.check(&input_choice(), event.target.checked())
63        })?;
64        let node = self.element(id)?;
65        self.watch(move || {
66            node.set_checked(model.checked(&choice()));
67            Ok(())
68        })
69    }
70    pub fn value<T: Display>(
71        &mut self,
72        id: usize,
73        read: impl Fn() -> T + 'static,
74    ) -> Result<(), Error> {
75        self.effect(self.element(id)?, move |node| {
76            node.set_value(read().to_string())
77        })
78    }
79    pub fn checked(&mut self, id: usize, read: impl Fn() -> bool + 'static) -> Result<(), Error> {
80        self.effect(self.element(id)?, move |node| node.set_checked(read()))
81    }
82    pub fn attribute<T: Display>(
83        &mut self,
84        id: usize,
85        name: &'static str,
86        read: impl Fn() -> T + 'static,
87    ) -> Result<(), Error> {
88        self.effect(self.element(id)?, move |node| {
89            node.set_attribute(name, Some(read().to_string()))
90        })
91    }
92    pub fn boolean(
93        &mut self,
94        id: usize,
95        name: &'static str,
96        read: impl Fn() -> bool + 'static,
97    ) -> Result<(), Error> {
98        self.effect(self.element(id)?, move |node| {
99            node.set_attribute(name, read().then(String::new))
100        })
101    }
102    pub fn class(
103        &mut self,
104        id: usize,
105        name: &'static str,
106        read: impl Fn() -> bool + 'static,
107    ) -> Result<(), Error> {
108        self.effect(self.element(id)?, move |node| {
109            node.set_attribute(
110                "class",
111                Some(crate::text::toggle_class(
112                    &node.attribute("class").unwrap_or_default(),
113                    name,
114                    read(),
115                )),
116            )
117        })
118    }
119}