Upgrade yew

This commit is contained in:
Thomas Gideon 2021-09-06 17:17:51 -04:00
parent 0ff7d59f63
commit dd6df30370
18 changed files with 129 additions and 71 deletions

View File

@ -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"

View File

@ -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())),

View File

@ -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) {}
}

View File

@ -133,6 +133,6 @@ mod tests {
>
</li>
};
assert_eq!(expected, item.view());
crate::test::assert_attrs_eq(expected, item.view());
}
}

View File

@ -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()
>

View File

@ -44,6 +44,6 @@ mod test {
</div>
};
assert_eq!(expected, comp.view());
crate::test::assert_attrs_eq(expected, comp.view());
}
}

View File

@ -41,6 +41,6 @@ mod test {
</p>
};
assert_eq!(expected, comp.view());
crate::test::assert_attrs_eq(expected, comp.view());
}
}

View File

@ -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());
}
}

View File

@ -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)

View File

@ -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)
}

View File

@ -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!"),
}
}
}

View File

@ -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")))
}
}

View File

@ -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

View File

@ -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))
}
}

View File

@ -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 {

View File

@ -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))
}
}

View File

@ -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 /> }),
);
}
}

View File

@ -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);
}
}