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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
use crate::callback::Callback;
use crate::html::{ChangeData, Component, ComponentLink, Html, Renderable, ShouldRender};
use crate::macros::{html, Properties};
pub struct Select<T> {
props: Props<T>,
}
pub enum Msg {
Selected(Option<usize>),
}
#[derive(PartialEq, Properties)]
pub struct Props<T> {
pub selected: Option<T>,
pub disabled: bool,
pub options: Vec<T>,
#[props(required)]
pub onchange: Callback<T>,
}
impl<T> Component for Select<T>
where
T: PartialEq + Clone + 'static,
{
type Message = Msg;
type Properties = Props<T>;
fn create(props: Self::Properties, _: ComponentLink<Self>) -> Self {
Self { props }
}
fn update(&mut self, msg: Self::Message) -> ShouldRender {
match msg {
Msg::Selected(value) => {
if let Some(idx) = value {
let item = self.props.options.get(idx - 1).cloned();
if let Some(value) = item {
self.props.onchange.emit(value);
}
}
}
}
true
}
fn change(&mut self, props: Self::Properties) -> ShouldRender {
self.props = props;
true
}
}
impl<T> Renderable<Select<T>> for Select<T>
where
T: ToString + PartialEq + Clone + 'static,
{
fn view(&self) -> Html<Self> {
let selected = self.props.selected.as_ref();
let view_option = |value: &T| {
let flag = selected == Some(value);
html! {
<option selected=flag>{ value.to_string() }</option>
}
};
html! {
<select disabled=self.props.disabled
onchange=|event| {
match event {
ChangeData::Select(elem) => {
let value = elem.selected_index().map(|x| x as usize);
Msg::Selected(value)
}
_ => {
unreachable!();
}
}
}>
<option disabled=true selected=selected.is_none()>
{ "↪" }
</option>
{ for self.props.options.iter().map(view_option) }
</select>
}
}
}