Improvements over HtmlSelect to allow using hashable objects (#47)

This commit is contained in:
Cecile Tonglet 2020-10-09 20:56:09 +02:00 committed by GitHub
parent 5b6b643290
commit 58933276c9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 87 additions and 29 deletions

View file

@ -3,6 +3,8 @@ use yewprint::HtmlSelect;
pub struct Example {
props: ExampleProps,
link: ComponentLink<Self>,
log_level: LogLevel,
}
#[derive(Clone, PartialEq, Properties)]
@ -14,14 +16,19 @@ pub struct ExampleProps {
}
impl Component for Example {
type Message = ();
type Message = LogLevel;
type Properties = ExampleProps;
fn create(props: Self::Properties, _link: ComponentLink<Self>) -> Self {
Example { props }
fn create(props: Self::Properties, link: ComponentLink<Self>) -> Self {
Example {
props,
link,
log_level: LogLevel::Info,
}
}
fn update(&mut self, _msg: Self::Message) -> ShouldRender {
fn update(&mut self, msg: Self::Message) -> ShouldRender {
self.log_level = msg;
true
}
@ -37,21 +44,34 @@ impl Component for Example {
fn view(&self) -> Html {
html! {
<div style="width: 400px; text-align: center;">
<HtmlSelect
<HtmlSelect<LogLevel>
options={vec![
("trace".to_string(), "TRACE".to_string()),
("debug".to_string(), "DEBUG".to_string()),
("info".to_string(), "INFO".to_string()),
("warn".to_string(), "WARN".to_string()),
("error".to_string(), "ERROR".to_string()),
("off".to_string(), "OFF".to_string()),
(LogLevel::Trace, "TRACE".to_string()),
(LogLevel::Debug, "DEBUG".to_string()),
(LogLevel::Info, "INFO".to_string()),
(LogLevel::Warn, "WARN".to_string()),
(LogLevel::Error, "ERROR".to_string()),
(LogLevel::Off, "OFF".to_string()),
]}
minimal=self.props.minimal
fill=self.props.fill
disabled=self.props.disabled
large=self.props.large
value=Some(self.log_level)
onchange=self.link.callback(|x| x)
title=format!("Selected: {:?}", self.log_level)
/>
</div>
}
}
}
#[derive(Debug, Clone, Copy, Hash, PartialEq)]
pub enum LogLevel {
Trace,
Debug,
Info,
Warn,
Error,
Off,
}

View file

@ -1,12 +1,15 @@
use crate::{ConditionalClass, Icon, IconName};
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
use yew::prelude::*;
pub struct HtmlSelect {
props: Props,
pub struct HtmlSelect<T: Clone + PartialEq + Hash + 'static> {
props: Props<T>,
link: ComponentLink<Self>,
}
#[derive(Clone, PartialEq, Properties)]
pub struct Props {
pub struct Props<T: Clone + PartialEq + 'static> {
#[prop_or_default]
pub fill: ConditionalClass,
#[prop_or_default]
@ -20,23 +23,32 @@ pub struct Props {
#[prop_or_default]
pub title: Option<String>,
#[prop_or_default]
pub onchange: Callback<ChangeData>,
pub onchange: Callback<T>,
pub options: Vec<(T, String)>,
#[prop_or_default]
pub options: Vec<(String, String)>,
#[prop_or_default]
pub value: String,
pub value: Option<T>,
}
impl Component for HtmlSelect {
type Message = ();
type Properties = Props;
impl<T: Clone + PartialEq + Hash + 'static> Component for HtmlSelect<T> {
type Message = ChangeData;
type Properties = Props<T>;
fn create(props: Self::Properties, _link: ComponentLink<Self>) -> Self {
HtmlSelect { props }
fn create(props: Self::Properties, link: ComponentLink<Self>) -> Self {
Self { props, link }
}
fn update(&mut self, _msg: Self::Message) -> ShouldRender {
true
fn update(&mut self, msg: Self::Message) -> ShouldRender {
let i = if let ChangeData::Select(select) = msg {
select.selected_index()
} else {
unreachable!("unexpected ChangeData variant: {:?}", msg);
};
if i >= 0 {
let i = i as usize;
let variant = self.props.options[i].0.clone();
self.props.onchange.emit(variant);
}
false
}
fn change(&mut self, props: Self::Properties) -> ShouldRender {
@ -53,7 +65,25 @@ impl Component for HtmlSelect {
.props
.options
.iter()
.map(|(value, label)| html!(<option value=value>{label}</option>))
.map(|(value, label)| {
let selected = self
.props
.value
.as_ref()
.map(|x| value == x)
.unwrap_or_default();
let value = {
let mut hasher = DefaultHasher::new();
value.hash(&mut hasher);
hasher.finish()
};
html! {
<option selected=selected value=value>
{label}
</option>
}
})
.collect::<Html>();
html! {
@ -68,8 +98,16 @@ impl Component for HtmlSelect {
>
<select
disabled=*self.props.disabled
onchange={self.props.onchange.clone()}
value={self.props.value.clone()}
onchange={self.link.callback(|x| x)}
value?={
self.props.value
.as_ref()
.map(|value| {
let mut hasher = DefaultHasher::new();
value.hash(&mut hasher);
hasher.finish()
})
}
title?={self.props.title.clone()}
>
{option_children}