From 88e53935b452568f61661b4d611026aca4e4bf7b Mon Sep 17 00:00:00 2001 From: Thomas Gideon Date: Wed, 24 Jun 2020 17:16:43 -0400 Subject: [PATCH] Add input toolbar, button. --- Cargo.toml | 2 +- src/button/group.rs | 27 ++++++++++++++ src/button/mod.rs | 82 +++++++++++++++++++++++++++++++++++++++---- src/button/props.rs | 81 ++++++++++++++++++++++++++++++++++++++++++ src/button/toolbar.rs | 31 ++++++++++++++++ src/lib.rs | 2 +- src/render.rs | 9 +++-- 7 files changed, 223 insertions(+), 11 deletions(-) create mode 100644 src/button/group.rs create mode 100644 src/button/props.rs create mode 100644 src/button/toolbar.rs diff --git a/Cargo.toml b/Cargo.toml index 6643865..377d385 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bootstrap-rs" -version = "0.2.9" +version = "0.2.10" authors = ["Thomas Gideon "] edition = "2018" diff --git a/src/button/group.rs b/src/button/group.rs new file mode 100644 index 0000000..cfbee20 --- /dev/null +++ b/src/button/group.rs @@ -0,0 +1,27 @@ +use crate::{prelude::render_on_change, props::Props, render}; +use yew::prelude::*; + +pub struct ButtonGroup { + props: Props, +} + +impl Component for ButtonGroup { + type Properties = Props; + type Message = (); + + fn create(props: Self::Properties, _: ComponentLink) -> Self { + Self { props } + } + + fn update(&mut self, _: Self::Message) -> ShouldRender { + false + } + + fn change(&mut self, props: Self::Properties) -> ShouldRender { + render_on_change(&mut self.props, props) + } + + fn view(&self) -> Html { + render::render_with_prefix(&self.props, "btn-group", render::div(&self.props.children)) + } +} diff --git a/src/button/mod.rs b/src/button/mod.rs index cfbee20..114e5f6 100644 --- a/src/button/mod.rs +++ b/src/button/mod.rs @@ -1,19 +1,52 @@ -use crate::{prelude::render_on_change, props::Props, render}; +mod group; +mod props; +mod toolbar; + +use self::props::Props; +pub use self::{group::ButtonGroup, toolbar::ButtonToolbar}; +use crate::{prelude::render_on_change, render}; +use std::fmt::{Display, Formatter, Result}; use yew::prelude::*; -pub struct ButtonGroup { +#[derive(Clone, PartialEq)] +pub enum ButtonType { + Button, + Submit, + Reset, +} + +impl Default for ButtonType { + fn default() -> Self { + ButtonType::Button + } +} + +impl Display for ButtonType { + fn fmt(&self, f: &mut Formatter) -> Result { + use ButtonType::*; + match self { + Submit => write!(f, "submit"), + Reset => write!(f, "reset"), + _ => write!(f, "button"), + } + } +} + +pub struct Button { + link: ComponentLink, props: Props, } -impl Component for ButtonGroup { +impl Component for Button { type Properties = Props; type Message = (); - fn create(props: Self::Properties, _: ComponentLink) -> Self { - Self { props } + fn create(props: Self::Properties, link: ComponentLink) -> Self { + Self { link, props } } fn update(&mut self, _: Self::Message) -> ShouldRender { + self.props.on_click.emit(()); false } @@ -22,6 +55,43 @@ impl Component for ButtonGroup { } fn view(&self) -> Html { - render::render_with_prefix(&self.props, "btn-group", render::div(&self.props.children)) + let html = html! { + + }; + render::render_with_prefix(&self.props, self.calculate_prefix(), html) + } +} + +impl Button { + fn calculate_prefix(&self) -> Classes { + let mut prefix = Classes::from("btn"); + if self.props.active.unwrap_or_default() { + prefix.push("active"); + } + if let Some(ref color) = self.props.color { + prefix.push(&color.with_prefix("btn")); + } + if let Some(ref outline) = self.props.outline { + prefix.push(&outline.with_prefix("btn-outline")); + } + if self.props.text_nowrap.unwrap_or_default() { + prefix.push("text-nowrap"); + } + if self.props.small.unwrap_or_default() { + prefix.push("btn-sm"); + } + if self.props.large.unwrap_or_default() { + prefix.push("btn-lg"); + } + if self.props.block.unwrap_or_default() { + prefix.push("btn-block"); + } + prefix } } diff --git a/src/button/props.rs b/src/button/props.rs new file mode 100644 index 0000000..6248575 --- /dev/null +++ b/src/button/props.rs @@ -0,0 +1,81 @@ +use super::ButtonType; +use crate::{ + prelude::*, + props::{add_opt_attr, collect_props, BootstrapProps}, +}; +use std::collections::HashMap; +use yew::{html::Children, prelude::*}; + +#[derive(Properties, Clone, PartialEq, Default)] +pub struct Props { + // component specific + #[prop_or_default] + pub on_click: Callback<()>, + #[prop_or_default] + pub button_type: ButtonType, + #[prop_or_default] + pub active: Option, + #[prop_or_default] + pub disabled: Option, + #[prop_or_default] + pub text_nowrap: Option, + + // bootstrap specific + #[prop_or_default] + pub color: Option, + #[prop_or_default] + pub outline: Option, + #[prop_or_default] + pub small: Option, + #[prop_or_default] + pub large: Option, + #[prop_or_default] + pub block: Option, + #[prop_or_default] + pub border: Option, + #[prop_or_default] + pub borders: Vec, + #[prop_or_default] + pub margin: Option, + #[prop_or_default] + pub margins: Vec, + #[prop_or_default] + pub padding: Option, + #[prop_or_default] + pub paddings: Vec, + + // html specific + #[prop_or_default] + pub id: Option, + #[prop_or_default] + pub class: Classes, + #[prop_or_default] + pub style: Option, + #[prop_or_default] + pub aria_label: Option, + #[prop_or_default] + pub aria_describedby: Option, + #[prop_or_default] + pub children: Children, +} + +impl<'a> From<&'a Props> for BootstrapProps<'a> { + fn from(props: &Props) -> BootstrapProps { + let class = &props.class; + let borders = collect_props(&props.border, &props.borders); + let margins = collect_props(&props.margin, &props.margins); + let paddings = collect_props(&props.padding, &props.paddings); + let mut attributes = HashMap::new(); + add_opt_attr(&mut attributes, "id", &props.id); + add_opt_attr(&mut attributes, "style", &props.style); + add_opt_attr(&mut attributes, "aria-label", &props.aria_label); + add_opt_attr(&mut attributes, "aria-describedby", &props.aria_describedby); + BootstrapProps { + class, + borders, + margins, + paddings, + attributes, + } + } +} diff --git a/src/button/toolbar.rs b/src/button/toolbar.rs new file mode 100644 index 0000000..009e663 --- /dev/null +++ b/src/button/toolbar.rs @@ -0,0 +1,31 @@ +use crate::{prelude::render_on_change, props::Props, render}; +use yew::prelude::*; + +pub struct ButtonToolbar { + props: Props, +} + +impl Component for ButtonToolbar { + type Properties = Props; + type Message = (); + + fn create(props: Self::Properties, _: ComponentLink) -> Self { + Self { props } + } + + fn update(&mut self, _: Self::Message) -> ShouldRender { + false + } + + fn change(&mut self, props: Self::Properties) -> ShouldRender { + render_on_change(&mut self.props, props) + } + + fn view(&self) -> Html { + render::render_with_prefix( + &self.props, + "btn-toolbar", + render::div(&self.props.children), + ) + } +} diff --git a/src/lib.rs b/src/lib.rs index e10f458..90fd99a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,7 +13,7 @@ mod render; pub use self::{ alert::Alert, breadcrumb::{Breadcrumb, BreadcrumbItem}, - button::ButtonGroup, + button::{Button, ButtonGroup, ButtonToolbar}, card::{Card, CardBody, CardHeader, CardText}, container::Container, form::FormGroup, diff --git a/src/render.rs b/src/render.rs index e027597..7c4d872 100644 --- a/src/render.rs +++ b/src/render.rs @@ -36,14 +36,17 @@ pub(crate) fn p(children: &Children) -> Html { #[cfg(test)] mod tests { use super::*; - use crate::props::Props; + use crate::{prelude::*, props::Props}; #[test] fn test_multiple_prefixes() { - let props = Props::default(); + let props = Props { + margin: Some(Margin(Edge::All, 3)), + ..Props::default() + }; let comp = render_with_prefix(&props, vec!["first", "second"], html! {
}); let expected = html! { -
+
}; assert_eq!(expected, comp);