1use std::collections::BTreeMap;
2
3use hirola_core::{
4 effect::EffectAttribute,
5 generic_node::EventListener,
6 prelude::{GenericNode, MutableBTreeMap},
7};
8use hirola_dom::effects::attr_mixin::XEffect;
9use hirola_dom::{node_ref::NodeRef, Dom};
10use wasm_bindgen::JsCast;
11use web_sys::{Event, HtmlInputElement};
12
13pub struct Form;
14
15impl EffectAttribute for Form {
16 type Handler = XEffect;
17 fn read_as_attr(&self) -> String {
18 "form".to_string()
19 }
20}
21
22#[derive(Clone, Debug)]
24pub struct FormHandler<T: FormEntity> {
25 node_ref: NodeRef,
26 pub value: MutableBTreeMap<T::Columns, String>,
27}
28
29impl<T: FormEntity + Clone> FormHandler<T> {
30 pub fn new(value: T) -> Self {
32 Self {
33 node_ref: NodeRef::new(),
34 value: MutableBTreeMap::with_values(value.into()),
35 }
36 }
37
38 pub fn update_field(&self, name: T::Columns, value: String) {
39 self.value.lock_mut().insert_cloned(name, value);
40 }
41
42 pub fn get_value_by_field(&self, name: &T::Columns) -> Option<String> {
44 self.value.lock_ref().get(name).cloned()
45 }
46}
47impl<T: FormEntity + Clone> FormHandler<T> {
48 pub fn current(&self) -> T {
49 self.value.lock_ref().clone().into()
50 }
51}
52
53pub trait FormColumn {
54 fn name(&self) -> &str;
55}
56
57pub trait FormEntity:
58 From<BTreeMap<Self::Columns, String>> + Into<BTreeMap<Self::Columns, String>>
59{
60 type Columns: FormColumn + Copy + Ord;
61}
62
63impl<T: FormEntity + Clone> FormHandler<T>
64where
65 T::Columns: 'static,
66 T: 'static,
67{
68 pub fn bind(&self, column: T::Columns) -> Box<dyn FnOnce(&Dom)> {
69 let form = self.clone();
70 let cb = move |dom: &Dom| {
71 let f = form.clone();
72 let handler = Box::new(move |e: Event| {
73 let input = e
74 .current_target()
75 .unwrap()
76 .dyn_into::<HtmlInputElement>()
77 .unwrap();
78 let new_value = input.value();
79 f.update_field(column, new_value);
80 });
81 dom.event("input", handler);
82 let value: String = form.get_value_by_field(&column).unwrap_or_default();
83 dom.set_attribute("value", &value);
84
85 dom.set_attribute("name", &column.name());
86 };
87
88 Box::new(cb)
89 }
90
91 pub fn node_ref(&self) -> NodeRef {
93 self.node_ref.clone()
94 }
95}