1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
// This hook is in freya-components instead of freya-hooks because it uses some component props.

use std::{
    collections::HashMap,
    fmt::Display,
    hash::Hash,
};

use dioxus::prelude::*;

use crate::{
    ButtonProps,
    InputMode,
    InputProps,
};

type SubmitCallback<Id> = Box<dyn Fn(&HashMap<Id, String>)>;

/// Form controller
///
/// Use [`Self::input()`] to register inputs
/// And [`Self::submit()`] to register a submitter button
#[derive(Clone)]
pub struct UseForm<Id: Hash + Eq + 'static> {
    data: Signal<HashMap<Id, String>>,
    onsubmit: Signal<SubmitCallback<Id>>,
}

impl<Id: Clone + Hash + Eq + Display> UseForm<Id> {
    /// Register an Input component
    pub fn input(&self, id: Id) -> InputProps {
        let value = self.data.read().get(&id).cloned().unwrap_or_default();
        let placeholder = id.to_string();
        let mut data = self.data;
        InputProps {
            onchange: EventHandler::new(move |txt| {
                data.write().insert(id.clone(), txt);
            }),
            theme: None,
            mode: InputMode::default(),
            value,
            placeholder: Some(placeholder),
        }
    }

    /// Register a Button component
    pub fn submit(&self) -> ButtonProps {
        let submit = self.onsubmit;
        let data = self.data;
        ButtonProps {
            theme: None,
            onpress: Some(EventHandler::new(move |_| {
                (submit.peek())(&data.read());
            })),
            children: Ok(VNode::placeholder()),
            onclick: None,
        }
    }
}

/// Create a Form controller with a submit callback.
pub fn use_form<Id: Hash + Eq + Clone>(
    onsubmit: impl Fn(&HashMap<Id, String>) + 'static,
) -> UseForm<Id> {
    use_hook(|| UseForm {
        data: Signal::new(HashMap::default()),
        onsubmit: Signal::new(Box::new(onsubmit)),
    })
}