mirror of
https://github.com/yewprint/yewprint
synced 2024-11-22 03:23:03 +00:00
Tag (#32)
This commit is contained in:
parent
8eca5094cb
commit
5b6b643290
10 changed files with 504 additions and 15 deletions
|
@ -7,6 +7,7 @@ use crate::divider::*;
|
|||
use crate::html_select::*;
|
||||
use crate::icon::*;
|
||||
use crate::progressbar::*;
|
||||
use crate::tag::*;
|
||||
use crate::text::*;
|
||||
use crate::tree::*;
|
||||
|
||||
|
@ -166,6 +167,11 @@ impl Component for App {
|
|||
href="#switch"
|
||||
onclick=self.link.callback(|_| Msg::GoToMenu(DocMenu::Switch))
|
||||
/>
|
||||
<MenuItem
|
||||
text={html!("Tag")}
|
||||
href="#tag"
|
||||
onclick=self.link.callback(|_| Msg::GoToMenu(DocMenu::Tag))
|
||||
/>
|
||||
<MenuItem
|
||||
text={html!("Text")}
|
||||
href="#text"
|
||||
|
@ -207,6 +213,7 @@ impl Component for App {
|
|||
DocMenu::Tree => html!(<TreeDoc />),
|
||||
DocMenu::Icon => html!(<IconDoc />),
|
||||
DocMenu::ProgressBar => html!(<ProgressBarDoc />),
|
||||
DocMenu::Tag => html!(<TagDoc />),
|
||||
DocMenu::Menu => html!(),
|
||||
}
|
||||
})
|
||||
|
@ -243,6 +250,8 @@ pub enum DocMenu {
|
|||
ProgressBar,
|
||||
#[to = "/#switch"]
|
||||
Switch,
|
||||
#[to = "/#tag"]
|
||||
Tag,
|
||||
#[to = "/#text"]
|
||||
Text,
|
||||
#[to = "/#tree"]
|
||||
|
|
|
@ -10,6 +10,7 @@ mod html_select;
|
|||
mod icon;
|
||||
mod progressbar;
|
||||
mod switch;
|
||||
mod tag;
|
||||
mod text;
|
||||
mod tree;
|
||||
|
||||
|
|
|
@ -81,7 +81,7 @@ crate::build_example_prop_component! {
|
|||
..props
|
||||
})
|
||||
checked=self.props.animate
|
||||
label="animate"
|
||||
label="Animate"
|
||||
/>
|
||||
<p>{"Select intent:"}</p>
|
||||
<Menu>
|
||||
|
|
100
yewprint-doc/src/tag/example.rs
Normal file
100
yewprint-doc/src/tag/example.rs
Normal file
|
@ -0,0 +1,100 @@
|
|||
use yew::prelude::*;
|
||||
use yewprint::{ConditionalClass, IconName, Intent, Tag};
|
||||
|
||||
pub struct Example {
|
||||
props: ExampleProps,
|
||||
link: ComponentLink<Self>,
|
||||
tags: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Properties)]
|
||||
pub struct ExampleProps {
|
||||
pub initial_tags: Vec<String>,
|
||||
pub active: bool,
|
||||
pub fill: bool,
|
||||
pub icon: ConditionalClass,
|
||||
pub intent: Option<Intent>,
|
||||
pub interactive: bool,
|
||||
pub large: bool,
|
||||
pub minimal: bool,
|
||||
pub multiline: bool,
|
||||
pub removable: ConditionalClass,
|
||||
pub right_icon: ConditionalClass,
|
||||
pub round: bool,
|
||||
pub reset_tags: u64,
|
||||
}
|
||||
|
||||
pub enum ExampleMsg {
|
||||
Remove(String),
|
||||
Click,
|
||||
}
|
||||
|
||||
impl Component for Example {
|
||||
type Message = ExampleMsg;
|
||||
type Properties = ExampleProps;
|
||||
|
||||
fn create(props: Self::Properties, link: ComponentLink<Self>) -> Self {
|
||||
let tags = props.initial_tags.clone();
|
||||
Example { props, link, tags }
|
||||
}
|
||||
|
||||
fn update(&mut self, msg: Self::Message) -> ShouldRender {
|
||||
match msg {
|
||||
ExampleMsg::Remove(label) => {
|
||||
self.tags = self
|
||||
.tags
|
||||
.clone()
|
||||
.into_iter()
|
||||
.filter(|l| *l != label)
|
||||
.collect()
|
||||
}
|
||||
ExampleMsg::Click => (),
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
fn change(&mut self, props: Self::Properties) -> ShouldRender {
|
||||
if self.props != props {
|
||||
if self.props.reset_tags != props.reset_tags {
|
||||
self.tags = props.initial_tags.clone();
|
||||
}
|
||||
self.props = props;
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn view(&self) -> Html {
|
||||
self.tags
|
||||
.iter()
|
||||
.map(|label| {
|
||||
let remove = {
|
||||
let label = label.clone();
|
||||
self.props.removable.map_some(
|
||||
self.link
|
||||
.callback(move |_| ExampleMsg::Remove(label.clone())),
|
||||
)
|
||||
};
|
||||
html! {
|
||||
<Tag
|
||||
active=self.props.active
|
||||
fill=self.props.fill
|
||||
icon=self.props.icon.map_some(IconName::Print)
|
||||
intent=self.props.intent
|
||||
interactive=self.props.interactive
|
||||
large=self.props.large
|
||||
minimal=self.props.minimal
|
||||
multiline=self.props.multiline
|
||||
right_icon=self.props.right_icon.map_some(IconName::Star)
|
||||
round=self.props.round
|
||||
onremove=remove
|
||||
onclick=self.link.callback(|_| ExampleMsg::Click)
|
||||
>
|
||||
{label.clone()}
|
||||
</Tag>
|
||||
}
|
||||
})
|
||||
.collect::<Html>()
|
||||
}
|
||||
}
|
228
yewprint-doc/src/tag/mod.rs
Normal file
228
yewprint-doc/src/tag/mod.rs
Normal file
|
@ -0,0 +1,228 @@
|
|||
mod example;
|
||||
|
||||
use crate::ExampleContainer;
|
||||
use example::*;
|
||||
use yew::prelude::*;
|
||||
use yewprint::{Button, Intent, Menu, MenuItem, Switch, H1, H5};
|
||||
|
||||
pub struct TagDoc {
|
||||
callback: Callback<ExampleProps>,
|
||||
state: ExampleProps,
|
||||
}
|
||||
|
||||
fn initial_tags() -> Vec<String> {
|
||||
vec![
|
||||
"Landscape".into(),
|
||||
"Bird".into(),
|
||||
"City".into(),
|
||||
"Bridge".into(),
|
||||
"Street".into(),
|
||||
"Why do you go away? So that you can come back. So that you can see the place you \
|
||||
came from with new eyes and extra colors. And the people there see you differently, \
|
||||
too. Coming back to where you started is not the same as never leaving."
|
||||
.into(),
|
||||
]
|
||||
}
|
||||
|
||||
impl Component for TagDoc {
|
||||
type Message = ExampleProps;
|
||||
type Properties = ();
|
||||
|
||||
fn create(_props: Self::Properties, link: ComponentLink<Self>) -> Self {
|
||||
TagDoc {
|
||||
callback: link.callback(|x| x),
|
||||
state: ExampleProps {
|
||||
initial_tags: initial_tags(),
|
||||
active: false,
|
||||
fill: false,
|
||||
icon: false.into(),
|
||||
intent: None,
|
||||
interactive: false,
|
||||
large: false,
|
||||
minimal: false,
|
||||
multiline: false,
|
||||
removable: false.into(),
|
||||
right_icon: false.into(),
|
||||
round: false,
|
||||
reset_tags: 0,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
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">{"Tag"}</H1>
|
||||
<ExampleContainer
|
||||
source=source
|
||||
props=Some(html!{
|
||||
<TagProps
|
||||
callback={self.callback.clone()}
|
||||
props=example_props.clone()
|
||||
/>
|
||||
})
|
||||
>
|
||||
<Example with example_props/>
|
||||
</ExampleContainer>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
crate::build_example_prop_component! {
|
||||
TagProps for ExampleProps =>
|
||||
fn view(&self) -> Html {
|
||||
html! {
|
||||
<div>
|
||||
<H5>{"Props"}</H5>
|
||||
<div>
|
||||
<Switch
|
||||
onclick=self.update_props(|props, _| ExampleProps {
|
||||
active: !props.active,
|
||||
..props
|
||||
})
|
||||
checked=self.props.active
|
||||
label="Active"
|
||||
/>
|
||||
<Switch
|
||||
onclick=self.update_props(|props, _| ExampleProps {
|
||||
fill: !props.fill,
|
||||
..props
|
||||
})
|
||||
checked=self.props.fill
|
||||
label="Fill"
|
||||
/>
|
||||
<Switch
|
||||
onclick=self.update_props(|props, _| ExampleProps {
|
||||
interactive: !props.interactive,
|
||||
..props
|
||||
})
|
||||
checked=self.props.interactive
|
||||
label="Interactive"
|
||||
/>
|
||||
<Switch
|
||||
onclick=self.update_props(|props, _| ExampleProps {
|
||||
large: !props.large,
|
||||
..props
|
||||
})
|
||||
checked=self.props.large
|
||||
label="Large"
|
||||
/>
|
||||
<Switch
|
||||
onclick=self.update_props(|props, _| ExampleProps {
|
||||
minimal: !props.minimal,
|
||||
..props
|
||||
})
|
||||
checked=self.props.minimal
|
||||
label="Minimal"
|
||||
/>
|
||||
<Switch
|
||||
onclick=self.update_props(|props, _| ExampleProps {
|
||||
multiline: !props.multiline,
|
||||
..props
|
||||
})
|
||||
checked=self.props.multiline
|
||||
label="Multiline"
|
||||
/>
|
||||
<Switch
|
||||
onclick=self.update_props(|props, _| ExampleProps {
|
||||
round: !props.round,
|
||||
..props
|
||||
})
|
||||
checked=self.props.round
|
||||
label="Round"
|
||||
/>
|
||||
<Switch
|
||||
onclick=self.update_props(|props, _| ExampleProps {
|
||||
removable: !props.removable,
|
||||
..props
|
||||
})
|
||||
checked=self.props.removable
|
||||
label="Removable"
|
||||
/>
|
||||
<Switch
|
||||
onclick=self.update_props(|props, _| ExampleProps {
|
||||
icon: !props.icon,
|
||||
..props
|
||||
})
|
||||
checked=self.props.icon
|
||||
label="Icon"
|
||||
/>
|
||||
<Switch
|
||||
onclick=self.update_props(|props, _| ExampleProps {
|
||||
right_icon: !props.right_icon,
|
||||
..props
|
||||
})
|
||||
checked=self.props.right_icon
|
||||
label="Right icon"
|
||||
/>
|
||||
<Button
|
||||
onclick=self.update_props(|props, _| ExampleProps {
|
||||
reset_tags: props.reset_tags + 1,
|
||||
..props
|
||||
})
|
||||
>
|
||||
{"Reset tags"}
|
||||
</Button>
|
||||
<p>{"Select intent:"}</p>
|
||||
<Menu>
|
||||
<MenuItem
|
||||
onclick=self.update_props(|props, _| ExampleProps {
|
||||
intent: None,
|
||||
..props
|
||||
})
|
||||
text=html!{"None"}
|
||||
/>
|
||||
<MenuItem
|
||||
onclick=self.update_props(|props, _| ExampleProps {
|
||||
intent: Some(Intent::Primary),
|
||||
..props
|
||||
})
|
||||
text=html!{"Primary"}
|
||||
intent=Intent::Primary
|
||||
/>
|
||||
<MenuItem
|
||||
onclick=self.update_props(|props, _| ExampleProps {
|
||||
intent: Some(Intent::Success),
|
||||
..props
|
||||
})
|
||||
text=html!{"Success"}
|
||||
intent=Intent::Success
|
||||
/>
|
||||
<MenuItem
|
||||
onclick=self.update_props(|props, _| ExampleProps {
|
||||
intent: Some(Intent::Warning),
|
||||
..props
|
||||
})
|
||||
text=html!{"Warning"}
|
||||
intent=Intent::Warning
|
||||
/>
|
||||
<MenuItem
|
||||
onclick=self.update_props(|props, _| ExampleProps {
|
||||
intent: Some(Intent::Danger),
|
||||
..props
|
||||
})
|
||||
text=html!{"Danger"}
|
||||
intent=Intent::Danger
|
||||
/>
|
||||
</Menu>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
}
|
|
@ -35,10 +35,9 @@ impl Component for Example {
|
|||
fn view(&self) -> Html {
|
||||
html! {
|
||||
<div style="width: 150px; height: 20px">
|
||||
<Text
|
||||
ellipsize=self.props.ellipsize
|
||||
text=&self.props.text
|
||||
/>
|
||||
<Text ellipsize=self.props.ellipsize>
|
||||
{&self.props.text}
|
||||
</Text>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,8 @@ pub struct Props {
|
|||
pub title: String,
|
||||
#[prop_or_default]
|
||||
pub onclick: Callback<MouseEvent>,
|
||||
#[prop_or_default]
|
||||
pub class: String,
|
||||
pub children: html::Children,
|
||||
}
|
||||
|
||||
|
@ -51,6 +53,7 @@ impl Component for Button {
|
|||
self.props.fill.map_some("bp3-fill"),
|
||||
self.props.minimal.map_some("bp3-minimal"),
|
||||
self.props.intent,
|
||||
self.props.class.clone(),
|
||||
)
|
||||
onclick={self.props.onclick.clone()}
|
||||
>
|
||||
|
|
|
@ -10,6 +10,7 @@ mod icon;
|
|||
mod menu;
|
||||
mod progressbar;
|
||||
mod switch;
|
||||
mod tag;
|
||||
mod text;
|
||||
#[cfg(feature = "tree")]
|
||||
mod tree;
|
||||
|
@ -28,6 +29,7 @@ pub use id_tree;
|
|||
pub use menu::*;
|
||||
pub use progressbar::*;
|
||||
pub use switch::*;
|
||||
pub use tag::*;
|
||||
pub use text::*;
|
||||
#[cfg(feature = "tree")]
|
||||
pub use tree::*;
|
||||
|
@ -35,6 +37,24 @@ pub use tree::*;
|
|||
use std::ops::{Deref, DerefMut, Not};
|
||||
use yew::virtual_dom::{Classes, Transformer, VComp};
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! if_html {
|
||||
(let $pat:pat = $cond:expr => $($body:tt)+) => {
|
||||
if let $pat = $cond {
|
||||
html!($($body)+)
|
||||
} else {
|
||||
html!()
|
||||
}
|
||||
};
|
||||
($cond:expr => $($body:tt)+) => {
|
||||
if $cond {
|
||||
html($(body)+)
|
||||
} else {
|
||||
html!()
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// NOTE: this class needs to become deprecated when the feature bool_to_option lands in stable
|
||||
//
|
||||
// https://github.com/rust-lang/rust/issues/64260
|
||||
|
|
107
yewprint/src/tag.rs
Normal file
107
yewprint/src/tag.rs
Normal file
|
@ -0,0 +1,107 @@
|
|||
use crate::{if_html, Button, ConditionalClass, Icon, IconName, Intent, Text};
|
||||
use yew::prelude::*;
|
||||
|
||||
pub struct Tag {
|
||||
props: Props,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Properties)]
|
||||
pub struct Props {
|
||||
pub children: Children,
|
||||
#[prop_or_default]
|
||||
// FIXME Not clear that this field has any effect without `interactive` on.
|
||||
pub active: ConditionalClass,
|
||||
#[prop_or_default]
|
||||
pub fill: ConditionalClass,
|
||||
#[prop_or_default]
|
||||
pub icon: Option<IconName>,
|
||||
#[prop_or_default]
|
||||
pub intent: Option<Intent>,
|
||||
#[prop_or_default]
|
||||
pub interactive: ConditionalClass,
|
||||
#[prop_or_default]
|
||||
pub large: ConditionalClass,
|
||||
#[prop_or_default]
|
||||
pub minimal: ConditionalClass,
|
||||
#[prop_or_default]
|
||||
pub multiline: ConditionalClass,
|
||||
#[prop_or_default]
|
||||
pub onclick: Callback<MouseEvent>,
|
||||
#[prop_or_default]
|
||||
pub onremove: Option<Callback<MouseEvent>>,
|
||||
#[prop_or_default]
|
||||
pub right_icon: Option<IconName>,
|
||||
#[prop_or_default]
|
||||
pub round: ConditionalClass,
|
||||
#[prop_or_default]
|
||||
pub title: Option<String>,
|
||||
}
|
||||
|
||||
impl Component for Tag {
|
||||
type Message = ();
|
||||
type Properties = Props;
|
||||
|
||||
fn create(props: Self::Properties, _link: ComponentLink<Self>) -> Self {
|
||||
Tag { 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 {
|
||||
let icon = if_html!(let Some(icon) = self.props.icon => <Icon icon=icon />);
|
||||
|
||||
let right_icon =
|
||||
if_html!(let Some(right_icon) = self.props.right_icon => <Icon icon=right_icon />);
|
||||
|
||||
let remove_button = if_html! {
|
||||
let Some(callback) = self.props.onremove.clone() =>
|
||||
html!(
|
||||
<Button
|
||||
class="bp3-tag-remove"
|
||||
onclick={callback}
|
||||
>
|
||||
<Icon icon=IconName::SmallCross />
|
||||
</Button>
|
||||
)
|
||||
};
|
||||
|
||||
html! {
|
||||
<span
|
||||
class=(
|
||||
"bp3-tag",
|
||||
self.props.intent,
|
||||
self.props.active.map_some("bp3-active"),
|
||||
self.props.fill.map_some("bp3-fill"),
|
||||
self.props.interactive.map_some("bp3-interactive"),
|
||||
self.props.large.map_some("bp3-large"),
|
||||
self.props.minimal.map_some("bp3-minimal"),
|
||||
self.props.round.map_some("bp3-round"),
|
||||
)
|
||||
onclick={self.props.onclick.clone()}
|
||||
>
|
||||
{icon}
|
||||
<Text
|
||||
class="bp3-fill"
|
||||
ellipsize={!self.props.multiline}
|
||||
title=self.props.title.clone()
|
||||
inline=true
|
||||
>
|
||||
{self.props.children.clone()}
|
||||
</Text>
|
||||
{remove_button}
|
||||
{right_icon}
|
||||
</span>
|
||||
}
|
||||
}
|
||||
}
|
|
@ -10,7 +10,14 @@ pub struct Props {
|
|||
#[prop_or_default]
|
||||
pub ellipsize: ConditionalClass,
|
||||
#[prop_or_default]
|
||||
pub text: String,
|
||||
pub children: Children,
|
||||
#[prop_or_default]
|
||||
pub class: String,
|
||||
/// Wrap text in `span` instead of `div`.
|
||||
#[prop_or_default]
|
||||
pub inline: bool,
|
||||
#[prop_or_default]
|
||||
pub title: Option<String>,
|
||||
}
|
||||
|
||||
impl Component for Text {
|
||||
|
@ -35,15 +42,30 @@ impl Component for Text {
|
|||
}
|
||||
|
||||
fn view(&self) -> Html {
|
||||
html! {
|
||||
<p
|
||||
class=(
|
||||
"bp3-text",
|
||||
self.props.ellipsize.map_some("bp3-text-overflow-ellipsis"),
|
||||
)
|
||||
>
|
||||
{self.props.text.clone()}
|
||||
</p>
|
||||
if self.props.inline {
|
||||
html! {
|
||||
<span
|
||||
class=(
|
||||
self.props.class.clone(),
|
||||
self.props.ellipsize.map_some("bp3-text-overflow-ellipsis"),
|
||||
)
|
||||
title?=self.props.title.clone()
|
||||
>
|
||||
{self.props.children.clone()}
|
||||
</span>
|
||||
}
|
||||
} else {
|
||||
html! {
|
||||
<div
|
||||
class=(
|
||||
self.props.class.clone(),
|
||||
self.props.ellipsize.map_some("bp3-text-overflow-ellipsis"),
|
||||
)
|
||||
title?=self.props.title.clone()
|
||||
>
|
||||
{self.props.children.clone()}
|
||||
</div>
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue