arwa/html/
html_form_element.rs

1use std::convert::TryFrom;
2
3use delegate::delegate;
4use wasm_bindgen::JsCast;
5
6use crate::console::{Write, Writer};
7use crate::html::{AutoComplete, GenericHtmlElement, HtmlElement};
8use crate::{Element, GenericElement, GenericNode, GlobalEventHandlers, InvalidCast, Node};
9
10#[derive(Clone, Copy, PartialEq, Eq, Debug)]
11pub enum FormMethod {
12    Get,
13    Post,
14    Dialog,
15}
16
17impl Default for FormMethod {
18    fn default() -> Self {
19        FormMethod::Get
20    }
21}
22
23#[derive(Clone)]
24pub struct HtmlFormElement {
25    inner: web_sys::HtmlFormElement,
26}
27
28impl HtmlFormElement {
29    // TODO: enum for encoding? Spec seems to only allow 3 specific values
30    // (`application/x-www-form-urlencoded`, `multipart/form-data`, `text/plain`).
31
32    delegate! {
33        target self.inner {
34            pub fn accept_charset(&self) -> String;
35
36            pub fn set_accept_charset(&self, accept_charset: &str);
37
38            pub fn action(&self) -> String;
39
40            pub fn set_action(&self, action: &str);
41
42            pub fn encoding(&self) -> String;
43
44            pub fn set_encoding(&self, encoding: &str);
45
46            pub fn no_validate(&self) -> bool;
47
48            pub fn set_no_validate(&self, no_validate: bool);
49
50            pub fn target(&self) -> String;
51
52            pub fn set_target(&self, target: &str);
53
54            pub fn check_validity(&self) -> bool;
55
56            pub fn report_validity(&self) -> bool;
57
58            pub fn reset(&self);
59        }
60    }
61
62    pub fn autocomplete(&self) -> AutoComplete {
63        match &*self.inner.autocomplete() {
64            "off" => AutoComplete::Off,
65            _ => AutoComplete::On,
66        }
67    }
68
69    pub fn set_autocomplete(&self, autocomplete: AutoComplete) {
70        let autocomplete = match autocomplete {
71            AutoComplete::On => "on",
72            AutoComplete::Off => "off",
73        };
74
75        self.inner.set_autocomplete(autocomplete);
76    }
77
78    pub fn method(&self) -> FormMethod {
79        match &*self.inner.method() {
80            "post" => FormMethod::Post,
81            "dialog" => FormMethod::Dialog,
82            _ => FormMethod::Get,
83        }
84    }
85
86    pub fn set_method(&self, method: FormMethod) {
87        let method = match method {
88            FormMethod::Get => "get",
89            FormMethod::Post => "post",
90            FormMethod::Dialog => "dialog",
91        };
92
93        self.inner.set_method(method);
94    }
95
96    pub fn elements(&self) -> FormControlElements {
97        FormControlElements {
98            inner: self.inner.elements().unchecked_into(),
99        }
100    }
101
102    pub fn submit(&self) {
103        // Despite the web_sys return type, I can find no indication in the spec that `submit` can
104        // actually error, so just unwrap for now.
105        self.inner.submit().unwrap();
106    }
107}
108
109impl_html_common_traits!(HtmlFormElement);
110
111pub struct FormControlElements {
112    inner: web_sys::HtmlFormControlsCollection,
113}
114
115impl FormControlElements {
116    // TODO: decide what to do about iteration, indexing
117    pub fn get(&self, id_or_name: &str) -> Option<FormControl> {
118        self.inner
119            .named_item(id_or_name)
120            .map(|inner| FormControl { inner })
121    }
122
123    pub fn len(&self) -> usize {
124        self.inner.length() as usize
125    }
126}
127
128impl Write for FormControlElements {
129    fn write(&self, writer: &mut Writer) {
130        writer.write_1(self.inner.as_ref());
131    }
132}
133
134pub struct FormControl {
135    inner: js_sys::Object,
136}
137
138impl TryFrom<FormControl> for RadioNodes {
139    type Error = InvalidCast<FormControl>;
140
141    fn try_from(value: FormControl) -> Result<Self, Self::Error> {
142        value
143            .inner
144            .dyn_into::<web_sys::RadioNodeList>()
145            .map(|inner| RadioNodes { inner })
146            .map_err(|inner| InvalidCast(FormControl { inner }))
147    }
148}
149
150impl TryFrom<FormControl> for GenericHtmlElement {
151    type Error = InvalidCast<FormControl>;
152
153    fn try_from(value: FormControl) -> Result<Self, Self::Error> {
154        value
155            .inner
156            .dyn_into::<web_sys::HtmlElement>()
157            .map(|e| e.into())
158            .map_err(|inner| InvalidCast(FormControl { inner }))
159    }
160}
161
162impl Write for FormControl {
163    fn write(&self, writer: &mut Writer) {
164        writer.write_1(self.inner.as_ref());
165    }
166}
167
168// TODO: impl TryFrom<FormControl> for: HtmlInputElement, HtmlButtonElement, HtmlFieldsetElement,
169// HtmlObjectElement, HtmlOutputElement, HtmlSelectElement, HtmlTextareaElement
170
171pub struct RadioNodes {
172    inner: web_sys::RadioNodeList,
173}
174
175impl RadioNodes {
176    delegate! {
177        target self.inner {
178            pub fn value(&self) -> String;
179
180            pub fn set_value(&self, value: &str);
181        }
182    }
183
184    pub fn get(&self, index: usize) -> Option<GenericNode> {
185        u32::try_from(index)
186            .ok()
187            .and_then(|index| self.inner.get(index))
188            .map(|e| e.into())
189    }
190
191    pub fn len(&self) -> usize {
192        self.inner.length() as usize
193    }
194
195    pub fn is_empty(&self) -> bool {
196        self.len() == 0
197    }
198
199    pub fn is_not_empty(&self) -> bool {
200        !self.is_empty()
201    }
202
203    pub fn first(&self) -> Option<GenericNode> {
204        self.get(0)
205    }
206
207    pub fn last(&self) -> Option<GenericNode> {
208        let len = self.len();
209
210        if len > 0 {
211            self.get(len - 1)
212        } else {
213            None
214        }
215    }
216
217    pub fn iter(&self) -> RadioNodesIter {
218        RadioNodesIter {
219            radio_nodes: self,
220            current: 0,
221        }
222    }
223}
224
225impl Write for RadioNodes {
226    fn write(&self, writer: &mut Writer) {
227        writer.write_1(self.inner.as_ref());
228    }
229}
230
231impl IntoIterator for RadioNodes {
232    type Item = GenericNode;
233    type IntoIter = RadioNodesIntoIter;
234
235    fn into_iter(self) -> Self::IntoIter {
236        RadioNodesIntoIter {
237            radio_nodes: self,
238            current: 0,
239        }
240    }
241}
242
243pub struct RadioNodesIter<'a> {
244    radio_nodes: &'a RadioNodes,
245    current: usize,
246}
247
248impl<'a> Iterator for RadioNodesIter<'a> {
249    type Item = GenericNode;
250
251    fn next(&mut self) -> Option<Self::Item> {
252        let current = self.current;
253
254        self.current += 1;
255
256        self.radio_nodes.get(current)
257    }
258}
259
260pub struct RadioNodesIntoIter {
261    radio_nodes: RadioNodes,
262    current: usize,
263}
264
265impl Iterator for RadioNodesIntoIter {
266    type Item = GenericNode;
267
268    fn next(&mut self) -> Option<Self::Item> {
269        let current = self.current;
270
271        self.current += 1;
272
273        self.radio_nodes.get(current)
274    }
275}