Input group & Control group (#68)

This commit is contained in:
Yohan Boogaert 2020-12-03 13:26:37 +01:00 committed by GitHub
parent 2f8cd1f358
commit 0a0b0b2a94
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 607 additions and 41 deletions

View file

@ -7,7 +7,7 @@ else
fi
if ! [ -f core.tgz ]; then
curl -o core.tgz https://registry.npmjs.org/@blueprintjs/core/-/core-3.30.0.tgz
curl -o core.tgz https://registry.npmjs.org/@blueprintjs/core/-/core-3.36.0.tgz
fi
if ! [ -f docs-theme.tgz ]; then

View file

@ -3,9 +3,11 @@ use crate::buttons::*;
use crate::callout::*;
use crate::card::*;
use crate::collapse::*;
use crate::control_group::*;
use crate::divider::*;
use crate::html_select::*;
use crate::icon::*;
use crate::input_group::*;
use crate::menu::*;
use crate::progressbar::*;
use crate::switch::*;
@ -105,13 +107,15 @@ impl Component for App {
<Menu>
<MenuItem
text={html!(go_to_theme_label)}
onclick=self.link.callback(|_| Msg::ToggleLight)
onclick=self.link
.callback(|_| Msg::ToggleLight)
icon=go_to_theme_icon
/>
<MenuItem
text={html!("Button")}
href="#button"
onclick=self.link.callback(|_| Msg::GoToMenu(DocMenu::Button))
text={html!("Button")}
href="#button"
onclick=self.link
.callback(|_| Msg::GoToMenu(DocMenu::Button))
/>
<MenuItem
text={html!("ButtonGroup")}
@ -137,6 +141,12 @@ impl Component for App {
onclick=self.link
.callback(|_| Msg::GoToMenu(DocMenu::Collapse))
/>
<MenuItem
text={html!("ControlGroup")}
href="#control-group"
onclick=self.link
.callback(|_| Msg::GoToMenu(DocMenu::ControlGroup))
/>
<MenuItem
text={html!("Divider")}
href="#divider"
@ -145,18 +155,27 @@ impl Component for App {
/>
<MenuItem
text={html!("HtmlSelect")}
href="#html-select"
onclick=self.link
.callback(|_| Msg::GoToMenu(DocMenu::HtmlSelect))
/>
<MenuItem
text={html!("Icon")}
href="#icon"
onclick=self.link.callback(|_| Msg::GoToMenu(DocMenu::Icon))
onclick=self.link
.callback(|_| Msg::GoToMenu(DocMenu::Icon))
/>
<MenuItem
text={html!("InputGroup")}
href="#input-group"
onclick=self.link
.callback(|_| Msg::GoToMenu(DocMenu::InputGroup))
/>
<MenuItem
text={html!("Menu")}
href="#menu"
onclick=self.link.callback(|_| Msg::GoToMenu(DocMenu::Menu))
onclick=self.link
.callback(|_| Msg::GoToMenu(DocMenu::Menu))
/>
<MenuItem
text={html!("ProgressBar")}
@ -167,17 +186,20 @@ impl Component for App {
<MenuItem
text={html!("Switch")}
href="#switch"
onclick=self.link.callback(|_| Msg::GoToMenu(DocMenu::Switch))
onclick=self.link
.callback(|_| Msg::GoToMenu(DocMenu::Switch))
/>
<MenuItem
text={html!("Tabs")}
href="#tabs"
onclick=self.link.callback(|_| Msg::GoToMenu(DocMenu::Tabs))
onclick=self.link
.callback(|_| Msg::GoToMenu(DocMenu::Tabs))
/>
<MenuItem
text={html!("Tag")}
href="#tag"
onclick=self.link.callback(|_| Msg::GoToMenu(DocMenu::Tag))
onclick=self.link
.callback(|_| Msg::GoToMenu(DocMenu::Tag))
/>
<MenuItem
text={html!("Text")}
@ -188,7 +210,8 @@ impl Component for App {
<MenuItem
text={html!("Tree")}
href="#tree"
onclick=self.link.callback(|_| Msg::GoToMenu(DocMenu::Tree))
onclick=self.link
.callback(|_| Msg::GoToMenu(DocMenu::Tree))
/>
// NOTE: thanks to keep this list of <MenuItem> sorted
// alphabetically (except for the light switch)
@ -213,16 +236,18 @@ impl Component for App {
DocMenu::Callout => html!(<CalloutDoc />),
DocMenu::Card => html!(<CardDoc />),
DocMenu::Collapse => html!(<CollapseDoc />),
DocMenu::HtmlSelect => html!(<HtmlSelectDoc />),
DocMenu::Text => html!(<TextDoc />),
DocMenu::ControlGroup => html!(<ControlGroupDoc />),
DocMenu::Divider => html!(<DividerDoc />),
DocMenu::Tree => html!(<TreeDoc />),
DocMenu::HtmlSelect => html!(<HtmlSelectDoc />),
DocMenu::Icon => html!(<IconDoc />),
DocMenu::InputGroup => html!(<InputGroupDoc />),
DocMenu::Menu => html!(<MenuDoc />),
DocMenu::ProgressBar => html!(<ProgressBarDoc />),
DocMenu::Switch => html!(<SwitchDoc />),
DocMenu::Tabs => html!(<TabsDoc />),
DocMenu::Tag => html!(<TagDoc />),
DocMenu::Menu => html!(<MenuDoc />),
DocMenu::Text => html!(<TextDoc />),
DocMenu::Tree => html!(<TreeDoc />),
}
})
/>
@ -246,12 +271,16 @@ pub enum DocMenu {
Card,
#[to = "/#collapse"]
Collapse,
#[to = "/#control-group"]
ControlGroup,
#[to = "/#html-select"]
HtmlSelect,
#[to = "/#divider"]
Divider,
#[to = "/#icon"]
Icon,
#[to = "/#input-group"]
InputGroup,
#[to = "/#menu"]
Menu,
#[to = "/#progress-bar"]

View file

@ -11,6 +11,7 @@ pub struct Example {
pub struct ExampleProps {
pub minimal: bool,
pub fill: bool,
pub disabled: bool,
}
pub enum Msg {
@ -54,6 +55,7 @@ impl Component for Example {
onclick=self.link.callback(|_| Msg::AddOne)
minimal=self.props.minimal
fill=self.props.fill
disabled=self.props.disabled
>
{"Add 1"}
</Button>

View file

@ -20,6 +20,7 @@ impl Component for ButtonDoc {
state: ExampleProps {
minimal: false,
fill: false,
disabled: false,
},
}
}
@ -63,27 +64,35 @@ impl Component for ButtonDoc {
crate::build_example_prop_component! {
ButtonProps for ExampleProps =>
fn view(&self) -> Html {
html! {
<div>
<H5>{"Props"}</H5>
<Switch
onclick=self.update_props(|props, _| ExampleProps {
minimal: !props.minimal,
..props
})
checked=self.props.minimal
label=html!("Minimal")
/>
<Switch
onclick=self.update_props(|props, _| ExampleProps {
fill: !props.fill,
..props
})
checked=self.props.fill
label=html!("Fill")
/>
</div>
}
fn view(&self) -> Html {
html! {
<div>
<H5>{"Props"}</H5>
<Switch
onclick=self.update_props(|props, _| ExampleProps {
minimal: !props.minimal,
..props
})
checked=self.props.minimal
label=html!("Minimal")
/>
<Switch
onclick=self.update_props(|props, _| ExampleProps {
fill: !props.fill,
..props
})
checked=self.props.fill
label=html!("Fill")
/>
<Switch
onclick=self.update_props(|props, _| ExampleProps {
disabled: !props.disabled,
..props
})
checked=self.props.disabled
label=html!("Disabled")
/>
</div>
}
}
}

View file

@ -0,0 +1,63 @@
use yew::prelude::*;
use yewprint::{Button, ControlGroup, HtmlSelect, IconName, InputGroup};
pub struct Example {
props: ExampleProps,
}
#[derive(Clone, PartialEq, Properties)]
pub struct ExampleProps {
pub fill: bool,
pub vertical: bool,
}
impl Component for Example {
type Message = ();
type Properties = ExampleProps;
fn create(props: Self::Properties, _link: ComponentLink<Self>) -> Self {
Example { props }
}
fn update(&mut self, _msg: Self::Message) -> ShouldRender {
true
}
fn change(&mut self, props: Self::Properties) -> ShouldRender {
if self.props != props {
self.props = props;
true
} else {
false
}
}
fn view(&self) -> Html {
html! {
<ControlGroup
fill=self.props.fill
vertical=self.props.vertical
>
<HtmlSelect<Option<Sorting>>
options={vec![
(None, "Filter".to_string()),
(Some(Sorting::NameAscending), "Name - ascending".to_string()),
(Some(Sorting::NameDescending), "Name - descending".to_string()),
(Some(Sorting::PriceAscending), "Price - ascending".to_string()),
(Some(Sorting::PriceDescending), "Price - descending".to_string()),
]}
/>
<InputGroup placeholder="Find filters..." />
<Button icon=IconName::ArrowRight />
</ControlGroup>
}
}
}
#[derive(Debug, Clone, Copy, Hash, PartialEq)]
pub enum Sorting {
NameAscending,
NameDescending,
PriceAscending,
PriceDescending,
}

View file

@ -0,0 +1,88 @@
mod example;
use crate::ExampleContainer;
use example::*;
use yew::prelude::*;
use yewprint::{Switch, H1, H5};
pub struct ControlGroupDoc {
callback: Callback<ExampleProps>,
state: ExampleProps,
}
impl Component for ControlGroupDoc {
type Message = ExampleProps;
type Properties = ();
fn create(_: Self::Properties, link: ComponentLink<Self>) -> Self {
ControlGroupDoc {
callback: link.callback(|x| x),
state: ExampleProps {
fill: false,
vertical: false,
},
}
}
fn update(&mut self, msg: Self::Message) -> ShouldRender {
self.state = msg;
true
}
fn change(&mut self, _props: Self::Properties) -> ShouldRender {
true
}
fn view(&self) -> Html {
let example_props = self.state.clone();
let source = crate::include_raw_html!(
concat!(env!("OUT_DIR"), "/", file!(), ".html"),
"bp3-code_block"
);
html! {
<div>
<H1 class="docs-title">{"ControlGroup"}</H1>
<ExampleContainer
source=source
props=Some(html! {
<ControlGroupProps
callback={self.callback.clone()}
props=example_props.clone()
>
</ControlGroupProps>
})
>
<Example with example_props />
</ExampleContainer>
</div>
}
}
}
crate::build_example_prop_component! {
ControlGroupProps for ExampleProps =>
fn view(&self) -> Html {
html! {
<div>
<H5>{"Props"}</H5>
<Switch
onclick=self.update_props(|props, _| ExampleProps {
fill: !props.fill,
..props
})
checked=self.props.fill
label=html!("Fill")
/>
<Switch
onclick=self.update_props(|props, _| ExampleProps {
vertical: !props.vertical,
..props
})
checked=self.props.vertical
label=html!("Vertical")
/>
</div>
}
}
}

View file

@ -0,0 +1,85 @@
use yew::prelude::*;
use yewprint::{Button, IconName, InputGroup, Tag};
pub struct Example {
props: ExampleProps,
}
#[derive(Clone, PartialEq, Properties)]
pub struct ExampleProps {
pub disabled: bool,
pub fill: bool,
pub large: bool,
pub small: bool,
pub round: bool,
}
impl Component for Example {
type Message = ();
type Properties = ExampleProps;
fn create(props: Self::Properties, _link: ComponentLink<Self>) -> Self {
Example { props }
}
fn update(&mut self, _msg: Self::Message) -> ShouldRender {
true
}
fn change(&mut self, props: Self::Properties) -> ShouldRender {
if self.props != props {
self.props = props;
true
} else {
false
}
}
fn view(&self) -> Html {
html! {
<>
<InputGroup
fill=self.props.fill
large=self.props.large
small=self.props.small
round=self.props.round
disabled=self.props.disabled
left_icon=IconName::Filter
placeholder={"Filter histogram..."}
/>
<InputGroup
fill=self.props.fill
large=self.props.large
small=self.props.small
round=self.props.round
disabled=self.props.disabled
placeholder={"Enter your password..."}
right_element=html! {
<Button
icon=IconName::Lock
minimal=true
disabled=self.props.disabled
/>
}
/>
<InputGroup
fill=self.props.fill
large=self.props.large
small=self.props.small
round=self.props.round
disabled=self.props.disabled
left_icon=IconName::Tag
placeholder={"Find tags"}
right_element=html! {
<Tag
minimal=true
round=self.props.round
>
{"10000"}
</Tag>
}
/>
</>
}
}
}

View file

@ -0,0 +1,115 @@
mod example;
use crate::ExampleContainer;
use example::*;
use yew::prelude::*;
use yewprint::{Switch, H1, H5};
pub struct InputGroupDoc {
callback: Callback<ExampleProps>,
state: ExampleProps,
}
impl Component for InputGroupDoc {
type Message = ExampleProps;
type Properties = ();
fn create(_: Self::Properties, link: ComponentLink<Self>) -> Self {
InputGroupDoc {
callback: link.callback(|x| x),
state: ExampleProps {
disabled: false,
fill: false,
large: false,
small: false,
round: false,
},
}
}
fn update(&mut self, msg: Self::Message) -> ShouldRender {
self.state = msg;
true
}
fn change(&mut self, _props: Self::Properties) -> ShouldRender {
true
}
fn view(&self) -> Html {
let example_props = self.state.clone();
let source = crate::include_raw_html!(
concat!(env!("OUT_DIR"), "/", file!(), ".html"),
"bp3-code-block"
);
html! {
<div>
<H1 class="docs-title">{"InputGroup"}</H1>
<ExampleContainer
source=source
props=Some(html! {
<InputGroupProps
callback={self.callback.clone()}
props=example_props.clone()
>
</InputGroupProps>
})
>
<Example with example_props />
</ExampleContainer>
</div>
}
}
}
crate::build_example_prop_component! {
InputGroupProps for ExampleProps =>
fn view(&self) -> Html {
html! {
<div>
<H5>{"Props"}</H5>
<Switch
onclick=self.update_props(|props, _| ExampleProps {
disabled: !props.disabled,
..props
})
checked=self.props.disabled
label=html!("Disabled")
/>
<Switch
onclick=self.update_props(|props, _| ExampleProps {
fill: !props.fill,
..props
})
checked=self.props.fill
label=html!("Fill")
/>
<Switch
onclick=self.update_props(|props, _| ExampleProps {
large: !props.large,
..props
})
checked=self.props.large
label=html!("Large")
/>
<Switch
onclick=self.update_props(|props, _| ExampleProps {
small: !props.small,
..props
})
checked=self.props.small
label=html!("Small")
/>
<Switch
onclick=self.update_props(|props, _| ExampleProps {
round: !props.round,
..props
})
checked=self.props.round
label=html!("Round")
/>
</div>
}
}
}

View file

@ -4,10 +4,12 @@ mod buttons;
mod callout;
mod card;
mod collapse;
mod control_group;
mod divider;
mod example;
mod html_select;
mod icon;
mod input_group;
mod menu;
mod progressbar;
mod switch;

View file

@ -13,6 +13,8 @@ pub struct Props {
#[prop_or_default]
pub minimal: bool,
#[prop_or_default]
pub disabled: bool,
#[prop_or_default]
pub icon: Option<IconName>,
#[prop_or_default]
pub intent: Option<Intent>,
@ -22,6 +24,7 @@ pub struct Props {
pub onclick: Callback<MouseEvent>,
#[prop_or_default]
pub class: String,
#[prop_or_default]
pub children: html::Children,
}
@ -53,6 +56,7 @@ impl Component for Button {
"bp3-button",
self.props.fill.as_some("bp3-fill"),
self.props.minimal.as_some("bp3-minimal"),
self.props.disabled.as_some("bp3-disabled"),
self.props.intent,
self.props.class.clone(),
)
@ -67,9 +71,17 @@ impl Component for Button {
html!()
}
}
<span class="bp3-button-text">
{self.props.children.clone()}
</span>
{
if self.props.children.is_empty() {
html! ()
} else {
html! {
<span class="bp3-button-text">
{self.props.children.clone()}
</span>
}
}
}
</button>
}
}

View file

@ -0,0 +1,52 @@
use boolinator::Boolinator;
use yew::prelude::*;
pub struct ControlGroup {
props: ControlGroupProps,
}
#[derive(Clone, PartialEq, Properties)]
pub struct ControlGroupProps {
#[prop_or_default]
pub fill: bool,
#[prop_or_default]
pub vertical: bool,
#[prop_or_default]
pub children: html::Children,
}
impl Component for ControlGroup {
type Message = ();
type Properties = ControlGroupProps;
fn create(props: Self::Properties, _link: ComponentLink<Self>) -> Self {
Self { props }
}
fn update(&mut self, _: Self::Message) -> ShouldRender {
true
}
fn change(&mut self, props: Self::Properties) -> ShouldRender {
if self.props != props {
self.props = props;
true
} else {
false
}
}
fn view(&self) -> Html {
html! {
<div
class=(
"bp3-control-group",
self.props.fill.as_some("bp3-fill"),
self.props.vertical.as_some("bp3-vertical"),
)
>
{self.props.children.clone()}
</div>
}
}
}

104
yewprint/src/input_group.rs Normal file
View file

@ -0,0 +1,104 @@
use crate::{Icon, IconName};
use boolinator::Boolinator;
use yew::prelude::*;
pub struct InputGroup {
props: InputGroupProps,
}
#[derive(Clone, PartialEq, Properties)]
pub struct InputGroupProps {
#[prop_or_default]
pub disabled: bool,
#[prop_or_default]
pub fill: bool,
#[prop_or_default]
pub large: bool,
#[prop_or_default]
pub small: bool,
#[prop_or_default]
pub round: bool,
#[prop_or_default]
pub placeholder: String,
#[prop_or_default]
pub left_icon: Option<IconName>,
#[prop_or_default]
pub left_element: Option<yew::virtual_dom::VNode>,
#[prop_or_default]
pub right_element: Option<yew::virtual_dom::VNode>,
}
impl Component for InputGroup {
type Message = ();
type Properties = InputGroupProps;
fn create(props: Self::Properties, _link: ComponentLink<Self>) -> Self {
Self { props }
}
fn update(&mut self, _: Self::Message) -> ShouldRender {
true
}
fn change(&mut self, props: Self::Properties) -> ShouldRender {
if self.props != props {
self.props = props;
true
} else {
false
}
}
fn view(&self) -> Html {
html! {
<div
class=(
"bp3-input-group",
self.props.disabled.as_some("bp3-disabled"),
self.props.fill.as_some("bp3-fill"),
self.props.large.as_some("bp3-large"),
self.props.small.as_some("bp3-small"),
self.props.round.as_some("bp3-round"),
self.props.placeholder.clone()
)
>
{
if let Some(left_element) = self.props.left_element.clone() {
html! {
<span class="bp3-input-left-container">
{left_element}
</span>
}
} else {
html!()
}
}
{
if let Some(icon) = self.props.left_icon {
html! {
<Icon icon=icon />
}
} else {
html!()
}
}
<input
class="bp3-input"
placeholder=&self.props.placeholder
disabled=self.props.disabled
/>
{
if let Some(right_element) = self.props.right_element.clone() {
html! {
<span class="bp3-input-action">
{right_element}
</span>
}
} else {
html!()
}
}
</div>
}
}
}

View file

@ -3,10 +3,12 @@ mod buttons;
mod callout;
mod card;
mod collapse;
mod control_group;
mod divider;
mod html_elements;
mod html_select;
mod icon;
mod input_group;
mod menu;
mod progressbar;
mod switch;
@ -21,12 +23,14 @@ pub use buttons::*;
pub use callout::*;
pub use card::*;
pub use collapse::*;
pub use control_group::*;
pub use divider::*;
pub use html_elements::*;
pub use html_select::*;
pub use icon::*;
#[cfg(feature = "tree")]
pub use id_tree;
pub use input_group::*;
pub use menu::*;
pub use progressbar::*;
pub use switch::*;

View file

@ -8,7 +8,8 @@ pub struct Tag {
#[derive(Clone, PartialEq, Properties)]
pub struct Props {
pub children: Children,
#[prop_or_default]
pub children: html::Children,
#[prop_or_default]
// FIXME Not clear that this field has any effect without `interactive` on.
pub active: bool,