yew_stdweb/html/listener/
mod.rs1#[macro_use]
2mod macros;
3
4use cfg_if::cfg_if;
5use cfg_match::cfg_match;
6
7cfg_if! {
8 if #[cfg(feature = "std_web")] {
9 mod listener_stdweb;
10
11 use stdweb::js;
12 use stdweb::unstable::{TryFrom, TryInto};
13 use stdweb::web::html_element::{InputElement, SelectElement, TextAreaElement};
14 use stdweb::web::{Element, EventListenerHandle, FileList, IElement, INode};
15 use stdweb::web::event::InputEvent;
16
17 pub use listener_stdweb::*;
18 } else if #[cfg(feature = "web_sys")] {
19 mod listener_web_sys;
20
21 use wasm_bindgen::JsCast;
22 use web_sys::{
23 Element, FileList, HtmlInputElement as InputElement, HtmlSelectElement as SelectElement,
24 HtmlTextAreaElement as TextAreaElement,
25 InputEvent
26 };
27
28 pub use listener_web_sys::*;
29 }
30}
31
32#[derive(Debug)]
34pub struct InputData {
35 pub value: String,
38 pub event: InputEvent,
40}
41
42#[derive(Debug)]
51pub enum ChangeData {
52 Value(String),
54 Select(SelectElement),
58 Files(FileList),
60}
61
62fn oninput_handler(this: &Element, event: InputEvent) -> InputData {
63 let (v1, v2) = cfg_match! {
68 feature = "std_web" => ({
69 (
70 this.clone()
71 .try_into()
72 .map(|input: InputElement| input.raw_value())
73 .ok(),
74 this.clone()
75 .try_into()
76 .map(|input: TextAreaElement| input.value())
77 .ok(),
78 )
79 }),
80 feature = "web_sys" => ({
81 (
82 this.dyn_ref().map(|input: &InputElement| input.value()),
83 this.dyn_ref().map(|input: &TextAreaElement| input.value()),
84 )
85 }),
86 };
87 let v3 = this.text_content();
88 let value = v1.or(v2).or(v3)
89 .expect("only an InputElement or TextAreaElement or an element with contenteditable=true can have an oninput event listener");
90 InputData { value, event }
91}
92
93fn onchange_handler(this: &Element) -> ChangeData {
94 match this.node_name().as_ref() {
95 "INPUT" => {
96 let input = cfg_match! {
97 feature = "std_web" => InputElement::try_from(this.clone()).unwrap(),
98 feature = "web_sys" => this.dyn_ref::<InputElement>().unwrap(),
99 };
100 let is_file = input
101 .get_attribute("type")
102 .map(|value| value.eq_ignore_ascii_case("file"))
103 .unwrap_or(false);
104 if is_file {
105 let files: FileList = cfg_match! {
106 feature = "std_web" => js!( return @{input}.files; ).try_into().unwrap(),
107 feature = "web_sys" => input.files().unwrap(),
108 };
109 ChangeData::Files(files)
110 } else {
111 cfg_match! {
112 feature = "std_web" => ChangeData::Value(input.raw_value()),
113 feature = "web_sys" => ChangeData::Value(input.value()),
114 }
115 }
116 }
117 "TEXTAREA" => {
118 let tae = cfg_match! {
119 feature = "std_web" => TextAreaElement::try_from(this.clone()).unwrap(),
120 feature = "web_sys" => this.dyn_ref::<TextAreaElement>().unwrap(),
121 };
122 ChangeData::Value(tae.value())
123 }
124 "SELECT" => {
125 let se = cfg_match! {
126 feature = "std_web" => SelectElement::try_from(this.clone()).unwrap(),
127 feature = "web_sys" => this.dyn_ref::<SelectElement>().unwrap().clone(),
128 };
129 ChangeData::Select(se)
130 }
131 _ => {
132 panic!("only an InputElement, TextAreaElement or SelectElement can have an onchange event listener");
133 }
134 }
135}
136
137#[cfg(feature = "std_web")]
139#[derive(Debug)]
140pub struct EventListener(Option<EventListenerHandle>);
141
142#[cfg(feature = "std_web")]
143impl Drop for EventListener {
144 fn drop(&mut self) {
145 if let Some(event) = self.0.take() {
146 event.remove()
147 }
148 }
149}