Upgrade yew
This commit is contained in:
parent
0ff7d59f63
commit
dd6df30370
18 changed files with 129 additions and 71 deletions
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "bootstrap-rs"
|
||||
version = "0.4.0"
|
||||
version = "0.5.0"
|
||||
authors = ["Thomas Gideon <cmdln@thecommandline.net>"]
|
||||
edition = "2018"
|
||||
|
||||
|
@ -11,7 +11,7 @@ crate-type = ["cdylib", "rlib"]
|
|||
validate = [ "validator" ]
|
||||
|
||||
[dependencies]
|
||||
yew = "~0.17.2"
|
||||
yew = "^0.18.0"
|
||||
log = "~0.4.8"
|
||||
serde_json = "^1.0.40"
|
||||
serde = "^1.0.98"
|
||||
|
|
|
@ -17,10 +17,10 @@ pub enum Request {
|
|||
Clear,
|
||||
}
|
||||
|
||||
impl Into<Option<(Color, String)>> for &Request {
|
||||
fn into(self) -> Option<(Color, String)> {
|
||||
impl From<&Request> for Option<(Color, String)> {
|
||||
fn from(request: &Request) -> Option<(Color, String)> {
|
||||
use Request::*;
|
||||
match self {
|
||||
match request {
|
||||
Primary(alert) => Some((Color::Primary, alert.clone())),
|
||||
Secondary(alert) => Some((Color::Secondary, alert.clone())),
|
||||
Success(alert) => Some((Color::Success, alert.clone())),
|
||||
|
|
|
@ -11,6 +11,8 @@ use yew::prelude::*;
|
|||
pub struct Alert {
|
||||
link: ComponentLink<Self>,
|
||||
props: Props,
|
||||
prefix: String,
|
||||
prefixed_color: String,
|
||||
}
|
||||
|
||||
impl Component for Alert {
|
||||
|
@ -18,7 +20,12 @@ impl Component for Alert {
|
|||
type Message = ();
|
||||
|
||||
fn create(props: Self::Properties, link: ComponentLink<Self>) -> Self {
|
||||
Self { link, props }
|
||||
Self {
|
||||
link,
|
||||
props,
|
||||
prefix: String::from("alert"),
|
||||
prefixed_color: String::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn update(&mut self, _: Self::Message) -> ShouldRender {
|
||||
|
@ -27,6 +34,9 @@ impl Component for Alert {
|
|||
}
|
||||
|
||||
fn change(&mut self, props: Self::Properties) -> ShouldRender {
|
||||
if props.color != self.props.color {
|
||||
self.prefixed_color = props.color.with_prefix(&self.prefix);
|
||||
}
|
||||
render_if_ne(&mut self.props, props)
|
||||
}
|
||||
|
||||
|
@ -45,10 +55,10 @@ impl Component for Alert {
|
|||
</button>
|
||||
</div>
|
||||
};
|
||||
render::render_with_prefix(
|
||||
&self.props,
|
||||
vec!["alert", &self.props.color.with_prefix("alert")],
|
||||
html,
|
||||
)
|
||||
render::render_with_prefix(&self.props, vec![&self.prefix, &self.prefixed_color], html)
|
||||
}
|
||||
|
||||
fn rendered(&mut self, _first_render: bool) {}
|
||||
|
||||
fn destroy(&mut self) {}
|
||||
}
|
||||
|
|
|
@ -133,6 +133,6 @@ mod tests {
|
|||
>
|
||||
</li>
|
||||
};
|
||||
assert_eq!(expected, item.view());
|
||||
crate::test::assert_attrs_eq(expected, item.view());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,8 +5,11 @@ mod toolbar;
|
|||
use self::props::Props;
|
||||
pub use self::{group::ButtonGroup, toolbar::ButtonToolbar};
|
||||
use crate::{prelude::*, render};
|
||||
use std::fmt::{Display, Formatter, Result};
|
||||
use yew::prelude::*;
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
fmt::{Display, Formatter, Result},
|
||||
};
|
||||
use yew::{html::IntoOptPropValue, prelude::*};
|
||||
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub enum ButtonType {
|
||||
|
@ -15,6 +18,27 @@ pub enum ButtonType {
|
|||
Reset,
|
||||
}
|
||||
|
||||
impl IntoOptPropValue<Cow<'static, str>> for ButtonType {
|
||||
fn into_opt_prop_value(self) -> Option<Cow<'static, str>> {
|
||||
match self {
|
||||
Self::Button => Some(Cow::from("button")),
|
||||
Self::Submit => Some(Cow::from("submit")),
|
||||
Self::Reset => Some(Cow::from("reset")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoOptPropValue<Cow<'static, str>> for &ButtonType {
|
||||
fn into_opt_prop_value(self) -> Option<Cow<'static, str>> {
|
||||
use ButtonType::*;
|
||||
match self {
|
||||
Button => Some(Cow::from("button")),
|
||||
Submit => Some(Cow::from("submit")),
|
||||
Reset => Some(Cow::from("reset")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ButtonType {
|
||||
fn default() -> Self {
|
||||
ButtonType::Button
|
||||
|
@ -57,7 +81,7 @@ impl Component for Button {
|
|||
fn view(&self) -> Html {
|
||||
let html = html! {
|
||||
<button
|
||||
button=self.props.button_type
|
||||
button=&self.props.button_type
|
||||
onclick=self.link.callback(|_| ())
|
||||
disabled=self.props.disabled.unwrap_or_default()
|
||||
>
|
||||
|
|
|
@ -44,6 +44,6 @@ mod test {
|
|||
</div>
|
||||
};
|
||||
|
||||
assert_eq!(expected, comp.view());
|
||||
crate::test::assert_attrs_eq(expected, comp.view());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,6 +41,6 @@ mod test {
|
|||
</p>
|
||||
};
|
||||
|
||||
assert_eq!(expected, comp.view());
|
||||
crate::test::assert_attrs_eq(expected, comp.view());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,6 +43,6 @@ mod tests {
|
|||
<div class="container m-3 pt-3">
|
||||
</div>
|
||||
};
|
||||
assert_eq!(expected, container.view());
|
||||
crate::test::assert_attrs_eq(expected, container.view());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,9 +6,14 @@ use self::props::Props;
|
|||
pub use self::{group::InputGroup, textarea::TextArea};
|
||||
use crate::{prelude::*, render};
|
||||
use std::fmt::{Display, Formatter, Result as FmtResult};
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
fmt::{Display, Formatter, Result as FmtResult},
|
||||
};
|
||||
#[cfg(feature = "validate")]
|
||||
use validator::ValidationErrors;
|
||||
use yew::prelude::*;
|
||||
use yew::{html::IntoOptPropValue, prelude::*};
|
||||
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub enum InputType {
|
||||
|
@ -19,6 +24,18 @@ pub enum InputType {
|
|||
Color,
|
||||
}
|
||||
|
||||
impl IntoOptPropValue<Cow<'static, str>> for &InputType {
|
||||
fn into_opt_prop_value(self) -> Option<Cow<'static, str>> {
|
||||
match self {
|
||||
InputType::Text => Some(Cow::from("text")),
|
||||
InputType::Date => Some(Cow::from("date")),
|
||||
InputType::DateTime => Some(Cow::from("datetime-local")),
|
||||
InputType::Checkbox => Some(Cow::from("checkbox")),
|
||||
InputType::Color => Some(Cow::from("color")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for InputType {
|
||||
fn fmt(&self, f: &mut Formatter) -> FmtResult {
|
||||
match self {
|
||||
|
@ -78,9 +95,9 @@ impl Component for Input {
|
|||
let html = html! {
|
||||
<input
|
||||
type=input_type
|
||||
value=&self.state
|
||||
value=self.state.clone()
|
||||
readonly=self.props.readonly
|
||||
onchange=self.link.callback(|evt| InputChange(evt))
|
||||
onchange=self.link.callback(InputChange)
|
||||
/>
|
||||
};
|
||||
render::render_with_prefix(&self.props, prefix, html)
|
||||
|
|
|
@ -40,16 +40,14 @@ impl Component for TextArea {
|
|||
|
||||
#[cfg(not(feature = "validate"))]
|
||||
fn view(&self) -> Html {
|
||||
let prefix = vec!["form-control", &valid_as_class(&self.props.valid)];
|
||||
let html = {
|
||||
html! {
|
||||
let prefix = vec!["form-control", valid_as_class(&self.props.valid)];
|
||||
let html = html! {
|
||||
<textarea
|
||||
readonly=self.props.readonly
|
||||
onchange=self.link.callback(|evt| InputChange(evt))
|
||||
onchange=self.link.callback(InputChange)
|
||||
>
|
||||
{ &self.state }
|
||||
</textarea>
|
||||
}
|
||||
};
|
||||
render::render_with_prefix(&self.props, prefix, html)
|
||||
}
|
||||
|
|
15
src/lib.rs
15
src/lib.rs
|
@ -20,3 +20,18 @@ pub use self::{
|
|||
input::{Input, InputGroup, TextArea},
|
||||
jumbotron::Jumbotron,
|
||||
};
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) mod test {
|
||||
use yew::virtual_dom::VNode;
|
||||
|
||||
pub(crate) fn assert_attrs_eq(expected: VNode, comp: VNode) {
|
||||
match (expected, comp) {
|
||||
(VNode::VTag(mut expected), VNode::VTag(mut comp)) => assert_eq!(
|
||||
expected.attributes.get_mut_index_map(),
|
||||
comp.attributes.get_mut_index_map()
|
||||
),
|
||||
_ => panic!("One or both components were not tags!"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
use super::{Color, Edge};
|
||||
use crate::props::IntoBsClass;
|
||||
use yew::Classes;
|
||||
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub struct Border(pub Edge, pub Color);
|
||||
|
||||
impl IntoBsClass for Border {
|
||||
fn as_classname(&self) -> String {
|
||||
let edge = match self.0 {
|
||||
impl From<&&Border> for Classes {
|
||||
fn from(border: &&Border) -> Classes {
|
||||
let edge = match border.0 {
|
||||
Edge::All => "border".to_owned(),
|
||||
_ => self.0.with_prefix("border"),
|
||||
_ => border.0.with_prefix("border"),
|
||||
};
|
||||
format!("{} {}", edge, self.1.with_prefix("border"))
|
||||
Classes::from(format!("{} {}", edge, border.1.with_prefix("border")))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
use yew::html::ImplicitClone;
|
||||
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub enum Color {
|
||||
Primary,
|
||||
|
@ -12,6 +14,8 @@ pub enum Color {
|
|||
Unset,
|
||||
}
|
||||
|
||||
impl ImplicitClone for Color {}
|
||||
|
||||
impl Default for Color {
|
||||
fn default() -> Self {
|
||||
Self::Unset
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
use crate::props::IntoBsClass;
|
||||
use yew::Classes;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct Margin(pub super::Edge, pub usize);
|
||||
|
||||
impl IntoBsClass for Margin {
|
||||
fn as_classname(&self) -> String {
|
||||
format!("m{}-{}", self.0, self.1)
|
||||
impl From<&&Margin> for Classes {
|
||||
fn from(margin: &&Margin) -> Classes {
|
||||
Classes::from(format!("m{}-{}", margin.0, margin.1))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,9 +15,9 @@ use yew::prelude::*;
|
|||
/// edit or correct as needed.
|
||||
pub struct InputString<S: AsRef<str>>(pub S);
|
||||
|
||||
impl<S: AsRef<str>> Into<Option<String>> for InputString<S> {
|
||||
fn into(self) -> Option<String> {
|
||||
let InputString(input) = self;
|
||||
impl<S: AsRef<str>> From<InputString<S>> for Option<String> {
|
||||
fn from(input: InputString<S>) -> Option<String> {
|
||||
let InputString(input) = input;
|
||||
if input.as_ref().is_empty() {
|
||||
None
|
||||
} else {
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
use crate::props::IntoBsClass;
|
||||
use yew::Classes;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct Padding(pub super::Edge, pub usize);
|
||||
|
||||
impl IntoBsClass for Padding {
|
||||
fn as_classname(&self) -> String {
|
||||
format!("p{}-{}", self.0, self.1)
|
||||
impl From<&&Padding> for Classes {
|
||||
fn from(padding: &&Padding) -> Classes {
|
||||
Classes::from(format!("p{}-{}", padding.0, padding.1))
|
||||
}
|
||||
}
|
||||
|
|
42
src/props.rs
42
src/props.rs
|
@ -4,10 +4,6 @@ use std::collections::HashMap;
|
|||
use validator::ValidationErrors;
|
||||
use yew::{prelude::*, virtual_dom::VNode};
|
||||
|
||||
pub(crate) trait IntoBsClass {
|
||||
fn as_classname(&self) -> String;
|
||||
}
|
||||
|
||||
pub(crate) fn collect_props<'a, T>(t: &'a Option<T>, ts: &'a [T]) -> Vec<&'a T> {
|
||||
if let Some(t) = t.as_ref() {
|
||||
let mut r = vec![t];
|
||||
|
@ -89,12 +85,12 @@ pub struct BootstrapProps<'a> {
|
|||
impl<'a> BootstrapProps<'a> {
|
||||
pub fn add_attributes(&self, html: &mut VNode) {
|
||||
if let VNode::VTag(tag) = html {
|
||||
let attrs = self
|
||||
.attributes
|
||||
.iter()
|
||||
.map(|(key, value)| (key.to_string(), (*value).to_owned()))
|
||||
.collect();
|
||||
tag.add_attributes(attrs);
|
||||
for key in self.attributes.keys() {
|
||||
if let Some(value) = &self.attributes.get(key) {
|
||||
let value = **value;
|
||||
tag.add_attribute(key, value.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -106,21 +102,15 @@ impl<'a> BootstrapProps<'a> {
|
|||
paddings,
|
||||
..
|
||||
} = self;
|
||||
let classes = prefix.into();
|
||||
let classes = classes.extend((*class).to_owned());
|
||||
let classes = classes.extend(into_classnames(borders));
|
||||
let classes = classes.extend(into_classnames(margins));
|
||||
classes.extend(into_classnames(paddings))
|
||||
let mut classes: Classes = prefix.into();
|
||||
classes.push((*class).to_owned());
|
||||
classes.extend(borders);
|
||||
classes.extend(margins);
|
||||
classes.extend(paddings);
|
||||
classes
|
||||
}
|
||||
}
|
||||
|
||||
fn into_classnames<C: IntoBsClass>(c: &[&C]) -> Classes {
|
||||
c.iter().fold(Classes::new(), |mut cs, c| {
|
||||
cs.push(&c.as_classname());
|
||||
cs
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn add_opt_attr<'a>(
|
||||
attrs: &mut HashMap<&str, &'a String>,
|
||||
name: &'static str,
|
||||
|
@ -146,9 +136,9 @@ mod test {
|
|||
<div class="" aria-label="override" />
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
crate::test::assert_attrs_eq(
|
||||
expected,
|
||||
crate::render::render_with_prefix(&props, "", html! { <div aria-label="test" /> })
|
||||
crate::render::render_with_prefix(&props, "", html! { <div aria-label="test" /> }),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -163,9 +153,9 @@ mod test {
|
|||
<div class="" aria-label="some-label" />
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
crate::test::assert_attrs_eq(
|
||||
expected,
|
||||
crate::render::render_with_prefix(&props, "", html! { <div /> })
|
||||
crate::render::render_with_prefix(&props, "", html! { <div /> }),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,13 +7,13 @@ pub(crate) fn render_with_prefix<'a, B: Into<BootstrapProps<'a>>, C: Into<Classe
|
|||
mut node: VNode,
|
||||
) -> VNode {
|
||||
let props = props.into();
|
||||
props.add_attributes(&mut node);
|
||||
if let VNode::VTag(tag) = &mut node {
|
||||
let classes = &props.calculate_classes(prefix);
|
||||
if !classes.is_empty() {
|
||||
tag.add_attribute("class", classes);
|
||||
tag.add_attribute("class", classes.to_string());
|
||||
}
|
||||
}
|
||||
props.add_attributes(&mut node);
|
||||
node
|
||||
}
|
||||
|
||||
|
@ -49,6 +49,6 @@ mod tests {
|
|||
<div class="first second m-3"/>
|
||||
};
|
||||
|
||||
assert_eq!(expected, comp);
|
||||
crate::test::assert_attrs_eq(expected, comp);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue