Yewprint upgrade 0.19 (#131)

Co-authored-by: Cecile Tonglet <cecile.tonglet@cecton.com>
Co-authored-by: Francois Stephany <francois@tamere.eu>
This commit is contained in:
Forest Anderson 2022-06-28 04:57:14 -04:00 committed by GitHub
parent 9407209aa2
commit 3f96db6a7e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
81 changed files with 2726 additions and 3824 deletions

766
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -18,11 +18,13 @@ default = ["tree"]
tree = ["id_tree"]
[dependencies]
web-sys = { version = "0.3", features = ["DomRect", "Element"] }
yew = "0.18"
web-sys = { version = "0.3", features = ["DomRect", "Element", "Event", "HtmlSelectElement"] }
# yew = { git = "https://github.com/yewstack/yew", branch = "master" }
yew = "0.19"
id_tree = { version = "1.7", optional = true }
yewtil = { version = "0.4", features = ["pure"] }
wasm-bindgen = "0.2"
gloo = "0.6"
[build-dependencies]
regex = { version = "1", default-features = false, features = ["std", "unicode-perl"] }

View file

@ -1,10 +1,5 @@
use std::borrow::Cow;
use yew::prelude::*;
pub struct ButtonGroup {
props: ButtonGroupProps,
}
#[derive(Clone, PartialEq, Properties)]
pub struct ButtonGroupProps {
#[prop_or_default]
@ -16,49 +11,28 @@ pub struct ButtonGroupProps {
#[prop_or_default]
pub large: bool,
#[prop_or_default]
pub style: Option<Cow<'static, str>>,
pub style: Option<String>,
#[prop_or_default]
pub children: html::Children,
#[prop_or_default]
pub class: Classes,
}
impl Component for ButtonGroup {
type Message = ();
type Properties = ButtonGroupProps;
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 {
#[function_component(ButtonGroup)]
pub fn button_group(props: &ButtonGroupProps) -> Html {
html! {
<div
class=classes!(
class={classes!(
"bp3-button-group",
self.props.minimal.then(|| "bp3-minimal"),
self.props.fill.then(|| "bp3-fill"),
self.props.large.then(|| "bp3-large"),
self.props.vertical.then(|| "bp3-vertical"),
self.props.class.clone(),
)
style=self.props.style.clone()
props.minimal.then(|| "bp3-minimal"),
props.fill.then(|| "bp3-fill"),
props.large.then(|| "bp3-large"),
props.vertical.then(|| "bp3-vertical"),
props.class.clone(),
)}
style={props.style.clone()}
>
{self.props.children.clone()}
{props.children.clone()}
</div>
}
}
}

View file

@ -1,11 +1,6 @@
use crate::{Icon, IconName, Intent, Spinner, ICON_SIZE_LARGE};
use std::borrow::Cow;
use yew::prelude::*;
pub struct Button {
props: ButtonProps,
}
#[derive(Clone, PartialEq, Properties)]
pub struct ButtonProps {
#[prop_or_default]
@ -35,79 +30,58 @@ pub struct ButtonProps {
#[prop_or_default]
pub class: Classes,
#[prop_or_default]
pub style: Option<Cow<'static, str>>,
pub style: Option<String>,
#[prop_or_default]
pub children: html::Children,
}
impl Component for Button {
type Message = ();
type Properties = ButtonProps;
fn create(props: Self::Properties, _link: ComponentLink<Self>) -> Self {
Button { 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 {
#[function_component(Button)]
pub fn button(props: &ButtonProps) -> Html {
html! {
<button
class=classes!(
class={classes!(
"bp3-button",
self.props.fill.then(|| "bp3-fill"),
self.props.minimal.then(|| "bp3-minimal"),
self.props.small.then(|| "bp3-small"),
self.props.outlined.then(|| "bp3-outlined"),
self.props.loading.then(|| "bp3-loading"),
self.props.large.then(|| "bp3-large"),
(self.props.active && !self.props.disabled).then(|| "bp3-active"),
self.props.disabled.then(|| "bp3-disabled"),
self.props.intent,
self.props.class.clone(),
)
style=self.props.style.clone()
onclick=(!self.props.disabled).then(|| self.props.onclick.clone())
props.fill.then(|| "bp3-fill"),
props.minimal.then(|| "bp3-minimal"),
props.small.then(|| "bp3-small"),
props.outlined.then(|| "bp3-outlined"),
props.loading.then(|| "bp3-loading"),
props.large.then(|| "bp3-large"),
(props.active && !props.disabled).then(|| "bp3-active"),
props.disabled.then(|| "bp3-disabled"),
props.intent,
props.class.clone(),
)}
style={props.style.clone()}
onclick={(!props.disabled).then(|| props.onclick.clone())}
>
{
self
.props
props
.loading
.then(|| html! {
<Spinner
class=classes!("bp3-button-spinner")
size=ICON_SIZE_LARGE as f32
class={classes!("bp3-button-spinner")}
size={ICON_SIZE_LARGE as f32}
/>
})
.unwrap_or_default()
}
{
if let Some(icon) = self.props.icon {
if let Some(icon) = props.icon {
html! {
<Icon icon=icon />
<Icon icon={icon} />
}
} else {
html!()
}
}
{
if self.props.children.is_empty() {
if props.children.is_empty() {
html! ()
} else {
html! {
<span class="bp3-button-text">
{for self.props.children.iter()}
{for props.children.iter()}
</span>
}
}
@ -115,4 +89,3 @@ impl Component for Button {
</button>
}
}
}

View file

@ -1,12 +1,7 @@
use crate::icon::ICON_SIZE_LARGE;
use crate::{Icon, IconName, Intent};
use std::borrow::Cow;
use yew::prelude::*;
pub struct Callout {
props: CalloutProps,
}
#[derive(Clone, PartialEq, Properties)]
pub struct CalloutProps {
#[prop_or_default]
@ -18,36 +13,17 @@ pub struct CalloutProps {
#[prop_or_default]
pub intent: Option<Intent>,
#[prop_or_default]
pub title: Option<Cow<'static, str>>,
pub title: Option<String>,
pub children: html::Children,
}
impl Component for Callout {
type Message = ();
type Properties = CalloutProps;
fn create(props: Self::Properties, _link: ComponentLink<Self>) -> Self {
Self { props }
}
fn update(&mut self, _msg: Self::Message) -> ShouldRender {
true
}
fn change(&mut self, props: Self::Properties) -> ShouldRender {
if self.props == props {
return false;
}
self.props = props;
true
}
fn view(&self) -> Html {
let icon = if self.props.without_icon {
#[function_component(Callout)]
pub fn callout(props: &CalloutProps) -> Html {
let icon = if props.without_icon {
None
} else {
self.props.icon.or_else(|| {
self.props.intent.map(|intent| match intent {
props.icon.or_else(|| {
props.intent.map(|intent| match intent {
Intent::Primary => IconName::InfoSign,
Intent::Success => IconName::Tick,
Intent::Warning => IconName::WarningSign,
@ -56,25 +32,24 @@ impl Component for Callout {
})
};
let classes = classes!(
self.props.class.clone(),
props.class.clone(),
"bp3-callout",
icon.map(|_| "bp3-callout-icon"),
self.props.intent,
props.intent,
);
html! {
<div class=classes>
<div class={classes}>
{
icon.iter()
.map(|name| html!{<Icon icon=*name icon_size=ICON_SIZE_LARGE/>})
.map(|name| html!{<Icon icon={*name} icon_size={ICON_SIZE_LARGE}/>})
.collect::<Html>()
}
{
self.props.title.iter()
props.title.iter()
.map(|title| html!{<h4 class={"bp3-heading"}>{title}</h4>})
.collect::<Html>()
}
{ self.props.children.clone() }
{ props.children.clone() }
</div>
}
}
}

View file

@ -14,42 +14,17 @@ pub struct CardProps {
pub children: html::Children,
}
pub struct Card {
props: CardProps,
}
impl Component for Card {
type Message = ();
type Properties = CardProps;
fn create(props: Self::Properties, _link: ComponentLink<Self>) -> Self {
Self { props }
}
fn update(&mut self, _msg: Self::Message) -> bool {
true
}
fn change(&mut self, props: Self::Properties) -> bool {
if self.props != props {
self.props = props;
true
} else {
false
}
}
fn view(&self) -> Html {
#[function_component(Card)]
pub fn card(props: &CardProps) -> Html {
html! {
<div class=classes!(
<div class={classes!(
"bp3-card",
self.props.class.clone(),
self.props.elevation,
self.props.interactive.then(|| "bp3-interactive"),
)
onclick={self.props.onclick.clone()}>
{self.props.children.clone()}
props.class.clone(),
props.elevation,
props.interactive.then(|| "bp3-interactive"),
)}
onclick={props.onclick.clone()}>
{props.children.clone()}
</div>
}
}
}

View file

@ -1,11 +1,7 @@
use yew::prelude::*;
pub struct Checkbox {
props: Props,
}
#[derive(Clone, PartialEq, Properties)]
pub struct Props {
pub struct CheckboxProps {
#[prop_or_default]
pub disabled: bool,
#[prop_or_default]
@ -15,56 +11,35 @@ pub struct Props {
#[prop_or_default]
pub checked: bool,
#[prop_or_default]
pub onchange: Callback<ChangeData>,
pub onchange: Callback<Event>,
#[prop_or_default]
pub label: yew::virtual_dom::VNode,
#[prop_or_default]
pub indeterminate_state: bool,
}
impl Component for Checkbox {
type Message = ();
type Properties = Props;
fn create(props: Self::Properties, _link: ComponentLink<Self>) -> Self {
Self { 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 {
#[function_component(Checkbox)]
pub fn checkbox(props: &CheckboxProps) -> Html {
html! {
<label
class=classes!(
class={classes!(
"bp3-control", "bp3-checkbox",
self.props.disabled.then(|| "bp3-disabled"),
self.props.inline.then(|| "bp3-inline"),
self.props.large.then(|| "bp3-large")
)
props.disabled.then(|| "bp3-disabled"),
props.inline.then(|| "bp3-inline"),
props.large.then(|| "bp3-large")
)}
>
<input
type="checkbox"
checked={self.props.checked}
onchange={self.props.onchange.clone()}
disabled=self.props.disabled
checked={props.checked}
onchange={props.onchange.clone()}
disabled={props.disabled}
/>
<span
class="bp3-control-indicator"
>
</span>
{self.props.label.clone()}
{props.label.clone()}
</label>
}
}
}

View file

@ -1,7 +1,7 @@
use std::time::Duration;
use gloo::timers::callback::Timeout;
use std::{convert::TryInto, time::Duration};
use web_sys::Element;
use yew::prelude::*;
use yew::services::*;
pub struct Collapse {
height: Height,
@ -11,10 +11,7 @@ pub struct Collapse {
height_when_open: Option<String>,
animation_state: AnimationState,
contents_ref: NodeRef,
callback_delayed_state_change: Callback<()>,
handle_delayed_state_change: Option<Box<dyn Task>>,
props: CollapseProps,
link: ComponentLink<Self>,
handle_delayed_state_change: Option<Timeout>,
}
#[derive(Clone, PartialEq, Properties)]
@ -52,33 +49,29 @@ impl Component for Collapse {
type Message = ();
type Properties = CollapseProps;
fn create(props: Self::Properties, link: ComponentLink<Self>) -> Self {
fn create(ctx: &Context<Self>) -> Self {
Collapse {
height: if props.is_open {
height: if ctx.props().is_open {
Height::Auto
} else {
Height::Zero
},
overflow_visible: false,
translated: false,
render_children: props.is_open || props.keep_children_mounted,
render_children: ctx.props().is_open || ctx.props().keep_children_mounted,
height_when_open: None,
animation_state: if props.is_open {
animation_state: if ctx.props().is_open {
AnimationState::Open
} else {
AnimationState::Closed
},
contents_ref: NodeRef::default(),
callback_delayed_state_change: link.callback(|_| ()),
handle_delayed_state_change: None,
props,
link,
}
}
fn change(&mut self, props: Self::Properties) -> ShouldRender {
if self.props != props {
if props.is_open {
fn changed(&mut self, ctx: &Context<Self>) -> bool {
if ctx.props().is_open {
match self.animation_state {
AnimationState::Open | AnimationState::Opening => {}
_ => {
@ -98,31 +91,41 @@ impl Component for Collapse {
}
}
self.props = props;
true
} else {
false
}
}
fn update(&mut self, _msg: Self::Message) -> ShouldRender {
fn update(&mut self, ctx: &Context<Self>, _msg: Self::Message) -> bool {
match self.animation_state {
AnimationState::OpenStart => {
let link = ctx.link().clone();
self.animation_state = AnimationState::Opening;
self.height = Height::Full;
self.handle_delayed_state_change = Some(Box::new(TimeoutService::spawn(
self.props.transition_duration,
self.callback_delayed_state_change.clone(),
)));
self.handle_delayed_state_change = Some(Timeout::new(
ctx.props()
.transition_duration
.as_millis()
.try_into()
.unwrap(),
move || {
link.send_message(());
},
));
true
}
AnimationState::ClosingStart => {
let link = ctx.link().clone();
self.animation_state = AnimationState::Closing;
self.height = Height::Zero;
self.handle_delayed_state_change = Some(Box::new(TimeoutService::spawn(
self.props.transition_duration,
self.callback_delayed_state_change.clone(),
)));
self.handle_delayed_state_change = Some(Timeout::new(
ctx.props()
.transition_duration
.as_millis()
.try_into()
.unwrap(),
move || {
link.send_message(());
},
));
true
}
AnimationState::Opening => {
@ -132,7 +135,7 @@ impl Component for Collapse {
}
AnimationState::Closing => {
self.animation_state = AnimationState::Closed;
if !self.props.keep_children_mounted {
if !ctx.props().keep_children_mounted {
self.render_children = false;
}
true
@ -141,19 +144,19 @@ impl Component for Collapse {
}
}
fn rendered(&mut self, _first_render: bool) {
fn rendered(&mut self, ctx: &Context<Self>, _first_render: bool) {
if self.render_children {
let client_height = self.contents_ref.cast::<Element>().unwrap().client_height();
self.height_when_open = Some(format!("{}px", client_height));
}
match self.animation_state {
AnimationState::OpenStart | AnimationState::ClosingStart => self.link.send_message(()),
AnimationState::OpenStart | AnimationState::ClosingStart => ctx.link().send_message(()),
_ => {}
}
}
fn view(&self) -> Html {
fn view(&self, ctx: &Context<Self>) -> Html {
let mut container_style = String::with_capacity(30);
match (self.height, self.height_when_open.as_ref()) {
(Height::Zero, _) => container_style.push_str("height: 0px; "),
@ -179,19 +182,19 @@ impl Component for Collapse {
}
html! {
<div class=classes!("bp3-collapse") style={container_style}>
<div class={classes!("bp3-collapse")} style={container_style}>
<div
class=classes!(
class={classes!(
"bp3-collapse-body",
self.props.class.clone(),
)
ctx.props().class.clone(),
)}
style={content_style}
aria-hidden={(!self.render_children).then(|| "true")}
ref={self.contents_ref.clone()}
>
{
if self.render_children {
self.props.children.clone()
ctx.props().children.clone()
} else {
Default::default()
}

View file

@ -1,9 +1,5 @@
use yew::prelude::*;
pub struct ControlGroup {
props: ControlGroupProps,
}
#[derive(Clone, PartialEq, Properties)]
pub struct ControlGroupProps {
#[prop_or_default]
@ -18,39 +14,18 @@ pub struct ControlGroupProps {
pub class: Classes,
}
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 {
#[function_component(ControlGroup)]
pub fn control_group(props: &ControlGroupProps) -> Html {
html! {
<div
class=classes!(
class={classes!(
"bp3-control-group",
self.props.fill.then(|| "bp3-fill"),
self.props.vertical.then(|| "bp3-vertical"),
self.props.class.clone(),
)
props.fill.then(|| "bp3-fill"),
props.vertical.then(|| "bp3-vertical"),
props.class.clone(),
)}
>
{self.props.children.clone()}
{props.children.clone()}
</div>
}
}
}

View file

@ -1,9 +1,5 @@
use yew::prelude::*;
pub struct Divider {
props: DividerProps,
}
#[derive(Clone, PartialEq, Properties)]
pub struct DividerProps {
#[prop_or_default]
@ -14,36 +10,15 @@ pub struct DividerProps {
pub class: Classes,
}
impl Component for Divider {
type Message = ();
type Properties = DividerProps;
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 {
#[function_component(Divider)]
pub fn view(props: &DividerProps) -> Html {
html! {
<span
class=classes!(
class={classes!(
"bp3-divider",
self.props.vertical.then(|| "bp3-vertical"),
self.props.class.clone(),
)
props.vertical.then(|| "bp3-vertical"),
props.class.clone(),
)}
/>
}
}
}

View file

@ -1,40 +1,36 @@
use yew::prelude::*;
use yewtil::{Pure, PureComponent};
macro_rules! build_component {
($name:ident, $props_name:ident, $tag:tt, $class:literal) => {
pub type $name = Pure<$props_name>;
#[derive(Debug, Clone, PartialEq, Properties)]
pub struct $props_name {
#[derive(Properties, PartialEq)]
pub struct ChildrenOnlyProps {
#[prop_or_default]
pub class: Classes,
#[prop_or_default]
pub children: html::Children,
}
impl PureComponent for $props_name {
fn render(&self) -> Html {
macro_rules! build_component {
($name:ident, $tag:tt, $class:literal) => {
#[function_component($name)]
pub fn $tag(props: &ChildrenOnlyProps) -> Html {
html! {
<$tag class=classes!($class, self.class.clone())>
{self.children.clone()}
<$tag class={classes!($class, props.class.clone())}>
{ props.children.clone() }
</$tag>
}
}
}
};
}
build_component!(H1, H1Props, h1, "bp3-heading");
build_component!(H2, H2Props, h2, "bp3-heading");
build_component!(H3, H3Props, h3, "bp3-heading");
build_component!(H4, H4Props, h4, "bp3-heading");
build_component!(H5, H5Props, h5, "bp3-heading");
build_component!(H6, H6Props, h6, "bp3-heading");
build_component!(H1, h1, "bp3-heading");
build_component!(H2, h2, "bp3-heading");
build_component!(H3, h3, "bp3-heading");
build_component!(H4, h4, "bp3-heading");
build_component!(H5, h5, "bp3-heading");
build_component!(H6, h6, "bp3-heading");
build_component!(Blockquote, BlockquoteProps, blockquote, "bp3-blockquote");
build_component!(Code, CodeProps, code, "bp3-code");
build_component!(Label, LabelProps, label, "bp3-label");
build_component!(Pre, PreProps, pre, "bp3-pre");
build_component!(Ol, OlProps, ol, "bp3-ol");
build_component!(Ul, UlProps, ul, "bp3-ul");
build_component!(Blockquote, blockquote, "bp3-blockquote");
build_component!(Code, code, "bp3-code");
build_component!(Label, label, "bp3-label");
build_component!(Pre, pre, "bp3-pre");
build_component!(Ol, ol, "bp3-ol");
build_component!(Ul, ul, "bp3-ul");

View file

@ -1,9 +1,11 @@
use std::marker::PhantomData;
use crate::{Icon, IconName};
use web_sys::HtmlSelectElement;
use yew::prelude::*;
pub struct HtmlSelect<T: Clone + PartialEq + 'static> {
props: HtmlSelectProps<T>,
link: ComponentLink<Self>,
phantom: PhantomData<T>,
}
#[derive(Clone, PartialEq, Properties)]
@ -30,51 +32,44 @@ pub struct HtmlSelectProps<T: Clone + PartialEq + 'static> {
}
impl<T: Clone + PartialEq + 'static> Component for HtmlSelect<T> {
type Message = ChangeData;
type Message = Event;
type Properties = HtmlSelectProps<T>;
fn create(props: Self::Properties, link: ComponentLink<Self>) -> Self {
Self { props, link }
fn create(_ctx: &Context<Self>) -> Self {
Self {
phantom: PhantomData,
}
}
fn update(&mut self, msg: Self::Message) -> ShouldRender {
let i = if let ChangeData::Select(select) = msg {
fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
let i = if let Some(select) = msg.target_dyn_into::<HtmlSelectElement>() {
select.selected_index()
} else {
unreachable!("unexpected ChangeData variant: {:?}", msg);
unreachable!("unexpected Event: {:?}", msg);
};
if i >= 0 {
let i = i as usize;
let variant = self.props.options[i].0.clone();
self.props.onchange.emit(variant);
let variant = ctx.props().options[i].0.clone();
ctx.props().onchange.emit(variant);
}
false
}
fn change(&mut self, props: Self::Properties) -> ShouldRender {
if self.props != props {
self.props = props;
true
} else {
false
}
}
fn view(&self) -> Html {
let option_children = self
.props
fn view(&self, ctx: &Context<Self>) -> Html {
let option_children = ctx
.props()
.options
.iter()
.map(|(value, label)| {
let selected = self
.props
let selected = ctx
.props()
.value
.as_ref()
.map(|x| value == x)
.unwrap_or_default();
html! {
<option selected=selected>
<option selected={selected}>
{label}
</option>
}
@ -83,24 +78,24 @@ impl<T: Clone + PartialEq + 'static> Component for HtmlSelect<T> {
html! {
<div
class=classes!(
class={classes!(
"bp3-html-select",
self.props.minimal.then(|| "bp3-minimal"),
self.props.large.then(|| "bp3-large"),
self.props.fill.then(|| "bp3-fill"),
self.props.disabled.then(|| "bp3-disabled"),
self.props.class.clone(),
)
ctx.props().minimal.then(|| "bp3-minimal"),
ctx.props().large.then(|| "bp3-large"),
ctx.props().fill.then(|| "bp3-fill"),
ctx.props().disabled.then(|| "bp3-disabled"),
ctx.props().class.clone(),
)}
>
<select
disabled=self.props.disabled
onchange={self.link.callback(|x| x)}
title={self.props.title.clone()}
disabled={ctx.props().disabled}
onchange={ctx.link().callback(|x| x)}
title={ctx.props().title.clone()}
value={"".to_string()}
>
{option_children}
</select>
<Icon icon=IconName::DoubleCaretVertical/>
<Icon icon={IconName::DoubleCaretVertical}/>
</div>
}
}

View file

@ -12,10 +12,6 @@ impl Default for IconName {
pub const ICON_SIZE_STANDARD: i32 = 16;
pub const ICON_SIZE_LARGE: i32 = 20;
pub struct Icon {
props: IconProps,
}
#[derive(Clone, PartialEq, Properties)]
pub struct IconProps {
pub icon: IconName,
@ -33,57 +29,37 @@ pub struct IconProps {
pub onclick: Callback<MouseEvent>,
}
impl Component for Icon {
type Message = ();
type Properties = IconProps;
fn create(props: Self::Properties, _link: ComponentLink<Self>) -> Self {
Icon { 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
#[function_component(Icon)]
pub fn icon(props: &IconProps) -> Html {
let paths = if props.icon_size == ICON_SIZE_STANDARD {
icon_svg_paths_16(props.icon)
} else {
false
}
}
fn view(&self) -> Html {
let paths = if self.props.icon_size == ICON_SIZE_STANDARD {
icon_svg_paths_16(self.props.icon)
} else {
icon_svg_paths_20(self.props.icon)
icon_svg_paths_20(props.icon)
};
let pixel_grid_size = if self.props.icon_size >= ICON_SIZE_LARGE {
let pixel_grid_size = if props.icon_size >= ICON_SIZE_LARGE {
ICON_SIZE_LARGE
} else {
ICON_SIZE_STANDARD
};
let icon_string = format!("{:?}", self.props.icon);
let icon_string = format!("{:?}", props.icon);
html! {
<span
class=classes!("bp3-icon", self.props.class.clone(), self.props.intent)
onclick=self.props.onclick.clone()
class={classes!("bp3-icon", props.class.clone(), props.intent)}
onclick={props.onclick.clone()}
>
<svg
fill={self.props.color.clone()}
fill={props.color.clone()}
data-icon={icon_string.clone()}
width={self.props.icon_size.to_string()}
height={self.props.icon_size.to_string()}
width={props.icon_size.to_string()}
height={props.icon_size.to_string()}
viewBox={format!("0 0 {x} {x}", x=pixel_grid_size)}
>
<desc>{self.props.title.clone().unwrap_or(icon_string)}</desc>
<desc>{props.title.clone().unwrap_or(icon_string)}</desc>
{
paths.iter()
.map(|x| html! {
<path d=*x fillRule="evenodd" />
<path d={*x} fillRule="evenodd" />
})
.collect::<Html>()
}
@ -91,4 +67,3 @@ impl Component for Icon {
</span>
}
}
}

View file

@ -4,8 +4,6 @@ use yew::prelude::*;
const MIN_HORIZONTAL_PADDING: i32 = 10;
pub struct InputGroup {
props: InputGroupProps,
link: ComponentLink<Self>,
left_element_ref: NodeRef,
left_element_width: Option<i32>,
right_element_ref: NodeRef,
@ -74,7 +72,7 @@ pub struct InputGroupProps {
#[prop_or_default]
pub input_type: TextInputType,
#[prop_or_default]
pub oninput: Callback<InputData>,
pub oninput: Callback<InputEvent>,
#[prop_or_default]
pub onkeyup: Callback<KeyboardEvent>,
#[prop_or_default]
@ -91,10 +89,8 @@ impl Component for InputGroup {
type Message = ();
type Properties = InputGroupProps;
fn create(props: Self::Properties, link: ComponentLink<Self>) -> Self {
fn create(_ctx: &Context<Self>) -> Self {
Self {
props,
link,
left_element_ref: Default::default(),
left_element_width: Default::default(),
right_element_ref: Default::default(),
@ -102,20 +98,11 @@ impl Component for InputGroup {
}
}
fn update(&mut self, _: Self::Message) -> ShouldRender {
fn update(&mut self, _ctx: &Context<Self>, _: Self::Message) -> bool {
true
}
fn change(&mut self, props: Self::Properties) -> ShouldRender {
if self.props != props {
self.props = props;
true
} else {
false
}
}
fn view(&self) -> Html {
fn view(&self, ctx: &Context<Self>) -> Html {
let input_style = match (self.left_element_width, self.right_element_width) {
(Some(left), None) => format!("padding-left:{}px", left.max(MIN_HORIZONTAL_PADDING)),
(None, Some(right)) => format!("padding-right:{}px", right.max(MIN_HORIZONTAL_PADDING)),
@ -129,53 +116,53 @@ impl Component for InputGroup {
html! {
<div
class=classes!(
class={classes!(
"bp3-input-group",
self.props.disabled.then(|| "bp3-disabled"),
self.props.fill.then(|| "bp3-fill"),
self.props.large.then(|| "bp3-large"),
self.props.small.then(|| "bp3-small"),
self.props.round.then(|| "bp3-round"),
self.props.placeholder.clone(),
self.props.class.clone(),
)
ctx.props().disabled.then(|| "bp3-disabled"),
ctx.props().fill.then(|| "bp3-fill"),
ctx.props().large.then(|| "bp3-large"),
ctx.props().small.then(|| "bp3-small"),
ctx.props().round.then(|| "bp3-round"),
ctx.props().placeholder.clone(),
ctx.props().class.clone(),
)}
>
{
if let Some(left_element) = self.props.left_element.clone() {
if let Some(left_element) = ctx.props().left_element.clone() {
html! {
<span
class="bp3-input-left-container"
ref=self.left_element_ref.clone()
ref={self.left_element_ref.clone()}
>
{left_element}
</span>
}
} else if let Some(icon) = self.props.left_icon {
} else if let Some(icon) = ctx.props().left_icon {
html! {
<Icon icon=icon />
<Icon icon={icon} />
}
} else {
html!()
}
}
<input
ref=self.props.input_ref.clone()
ref={ctx.props().input_ref.clone()}
class="bp3-input"
type=self.props.input_type.as_str()
placeholder=self.props.placeholder.clone()
disabled=self.props.disabled
oninput={self.props.oninput.clone()}
onkeyup={self.props.onkeyup.clone()}
onkeydown={self.props.onkeydown.clone()}
value=self.props.value.clone()
style=input_style
type={ctx.props().input_type.as_str()}
placeholder={ctx.props().placeholder.clone()}
disabled={ctx.props().disabled}
oninput={ctx.props().oninput.clone()}
onkeyup={ctx.props().onkeyup.clone()}
onkeydown={ctx.props().onkeydown.clone()}
value={ctx.props().value.clone()}
style={input_style}
/>
{
if let Some(right_element) = self.props.right_element.clone() {
if let Some(right_element) = ctx.props().right_element.clone() {
html! {
<span
class="bp3-input-action"
ref=self.right_element_ref.clone()
ref={self.right_element_ref.clone()}
>
{right_element}
</span>
@ -188,7 +175,7 @@ impl Component for InputGroup {
}
}
fn rendered(&mut self, _first_render: bool) {
fn rendered(&mut self, ctx: &Context<Self>, _first_render: bool) {
let left_old_value = self.left_element_width.take();
self.left_element_width = self
.left_element_ref
@ -203,7 +190,7 @@ impl Component for InputGroup {
if left_old_value != self.left_element_width || right_old_value != self.right_element_width
{
self.link.send_message(());
ctx.link().send_message(());
}
}
}

View file

@ -1,11 +1,6 @@
use crate::{Icon, IconName, Intent, H6};
use std::borrow::Cow;
use yew::prelude::*;
pub struct Menu {
props: MenuProps,
}
#[derive(Clone, PartialEq, Properties)]
pub struct MenuProps {
#[prop_or_default]
@ -17,46 +12,21 @@ pub struct MenuProps {
pub children: html::Children,
}
impl Component for Menu {
type Message = ();
type Properties = MenuProps;
fn create(props: Self::Properties, _link: ComponentLink<Self>) -> Self {
Menu { 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 {
#[function_component(Menu)]
pub fn menu(props: &MenuProps) -> Html {
html! {
<ul
class=classes!(
class={classes!(
"bp3-menu",
self.props.large.then(|| "bp3-large"),
self.props.class.clone(),
)
ref={self.props.r#ref.clone()}
props.large.then(|| "bp3-large"),
props.class.clone(),
)}
ref={props.r#ref.clone()}
>
{self.props.children.clone()}
{props.children.clone()}
</ul>
}
}
}
pub struct MenuItem {
props: MenuItemProps,
}
#[derive(Clone, PartialEq, Properties)]
pub struct MenuItemProps {
@ -71,7 +41,7 @@ pub struct MenuItemProps {
#[prop_or_default]
pub disabled: bool,
#[prop_or_default]
pub href: Option<Cow<'static, str>>,
pub href: Option<String>,
#[prop_or_default]
pub label: Option<yew::virtual_dom::VNode>,
#[prop_or_default]
@ -89,66 +59,46 @@ pub struct MenuItemProps {
// TODO: pub children: html::Children,
}
impl Component for MenuItem {
type Message = ();
type Properties = MenuItemProps;
fn create(props: Self::Properties, _link: ComponentLink<Self>) -> Self {
MenuItem { 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 {
#[function_component(MenuItem)]
pub fn menu_item(props: &MenuItemProps) -> Html {
html! {
<li>
<a
class=classes!(
class={classes!(
"bp3-menu-item",
self.props.active.then(|| "bp3-active"),
self.props.disabled.then(|| "bp3-disabled"),
self.props.intent
.or_else(|| self.props.active.then(|| Intent::Primary)),
self.props.class.clone(),
)
href={(!self.props.disabled).then(|| self.props.href.clone())}.flatten()
tabIndex={(!self.props.disabled).then(|| "0")}
onclick={(!self.props.disabled).then(|| self.props.onclick.clone())}
props.active.then(|| "bp3-active"),
props.disabled.then(|| "bp3-disabled"),
props.intent
.or_else(|| props.active.then(|| Intent::Primary)),
props.class.clone(),
)}
href={(!props.disabled).then(|| props.href.clone()).flatten()}
tabIndex={(!props.disabled).then(|| "0")}
onclick={(!props.disabled).then(|| props.onclick.clone())}
>
{
if let Some(icon_name) = self.props.icon {
if let Some(icon_name) = props.icon {
html! {
<Icon icon={icon_name} />
}
} else if let Some(html) = self.props.icon_html.clone() {
} else if let Some(html) = props.icon_html.clone() {
html
} else {
html! {
<Icon icon=IconName::Blank />
<Icon icon={IconName::Blank} />
}
}
}
<div class=classes!("bp3-text", "bp3-fill", self.props.text_class.clone())>
{self.props.text.clone()}
<div class={classes!("bp3-text", "bp3-fill", props.text_class.clone())}>
{props.text.clone()}
</div>
{
if let Some(label) = self.props.label.clone() {
if let Some(label) = props.label.clone() {
html! {
<span
class=classes!(
class={classes!(
"bp3-menu-item-label",
self.props.label_class.clone())
props.label_class.clone())}
>
{label}
</span>
@ -162,11 +112,6 @@ impl Component for MenuItem {
</li>
}
}
}
pub struct MenuDivider {
props: MenuDividerProps,
}
#[derive(Clone, PartialEq, Properties)]
pub struct MenuDividerProps {
@ -174,42 +119,21 @@ pub struct MenuDividerProps {
pub title: Option<yew::virtual_dom::VNode>,
}
impl Component for MenuDivider {
type Message = ();
type Properties = MenuDividerProps;
fn create(props: Self::Properties, _link: ComponentLink<Self>) -> Self {
Self { 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 {
#[function_component(MenuDivider)]
pub fn menu_divider(props: &MenuDividerProps) -> Html {
html! {
if let Some(title) = self.props.title.clone() {
{if let Some(title) = props.title.clone() {
html! {
<li
class=classes!("bp3-menu-header")
class={classes!("bp3-menu-header")}
>
<H6>{title}</H6>
</li>
}
} else {
html! {
<li class=classes!("bp3-menu-divider") />
}
}
}
<li class={classes!("bp3-menu-divider")} />
}
}}
}
}

View file

@ -1,7 +1,9 @@
use crate::{Button, ButtonGroup, ControlGroup, IconName, InputGroup, Intent};
use std::fmt::Display;
use std::marker::PhantomData;
use std::ops::{Add, Bound, RangeBounds, Sub};
use std::str::FromStr;
use web_sys::HtmlInputElement;
use yew::html::IntoPropValue;
use yew::prelude::*;
@ -16,9 +18,8 @@ where
+ PartialOrd
+ 'static,
{
props: NumericInputProps<T>,
link: ComponentLink<Self>,
input: String,
phantom: PhantomData<T>,
}
#[derive(Clone, PartialEq, Properties)]
@ -84,42 +85,46 @@ where
type Message = Msg;
type Properties = NumericInputProps<T>;
fn create(props: Self::Properties, link: ComponentLink<Self>) -> Self {
fn create(_ctx: &Context<Self>) -> Self {
Self {
props,
link,
input: Default::default(),
phantom: PhantomData,
}
}
fn update(&mut self, msg: Self::Message) -> ShouldRender {
match msg {
Msg::InputUpdate(new_value) => {
if let Ok(new_value) = new_value.trim().parse::<T>() {
self.update_value(new_value)
} else {
false
}
}
Msg::Up => self.update_value(self.props.value + self.props.increment),
Msg::Down => self.update_value(self.props.value - self.props.increment),
Msg::Noop => false,
}
}
fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
let mut update_value = |new_value| {
let new_value = ctx.props().bounds.clamp(new_value, ctx.props().increment);
fn change(&mut self, props: Self::Properties) -> ShouldRender {
if self.props != props {
if self.props.value != props.value {
self.input = props.value.to_string();
}
self.props = props;
if new_value != ctx.props().value {
self.input = new_value.to_string();
ctx.props().onchange.emit(new_value);
true
} else {
false
}
};
match msg {
Msg::InputUpdate(new_value) => {
if let Ok(new_value) = new_value.trim().parse::<T>() {
update_value(new_value)
} else {
false
}
}
Msg::Up => update_value(ctx.props().value + ctx.props().increment),
Msg::Down => update_value(ctx.props().value - ctx.props().increment),
Msg::Noop => false,
}
}
fn view(&self) -> Html {
fn changed(&mut self, ctx: &Context<Self>) -> bool {
self.input = ctx.props().value.to_string();
true
}
fn view(&self, ctx: &Context<Self>) -> Html {
let NumericInputProps {
value,
increment,
@ -127,8 +132,9 @@ where
disable_buttons,
buttons_on_the_left,
..
} = self.props;
let bounds = &self.props.bounds;
} = *ctx.props();
let bounds = &ctx.props().bounds;
let button_up_disabled = disabled || bounds.clamp(value + increment, increment) == value;
let button_down_disabled = disabled || bounds.clamp(value - increment, increment) == value;
@ -136,16 +142,16 @@ where
html!()
} else {
html! {
<ButtonGroup vertical=true class=classes!("bp3-fixed")>
<ButtonGroup vertical=true class={classes!("bp3-fixed")}>
<Button
icon=IconName::ChevronUp
disabled=button_up_disabled
onclick=self.link.callback(|_| Msg::Up)
icon={IconName::ChevronUp}
disabled={button_up_disabled}
onclick={ctx.link().callback(|_| Msg::Up)}
/>
<Button
icon=IconName::ChevronDown
disabled=button_down_disabled
onclick=self.link.callback(|_| Msg::Down)
icon={IconName::ChevronDown}
disabled={button_down_disabled}
onclick={ctx.link().callback(|_| Msg::Down)}
/>
</ButtonGroup>
}
@ -153,15 +159,18 @@ where
let input_group = html! {
<InputGroup
placeholder=self.props.placeholder.clone()
large=self.props.large
disabled=self.props.disabled
left_icon=self.props.left_icon
left_element=self.props.left_element.clone()
right_element=self.props.right_element.clone()
value=self.input.clone()
oninput=self.link.callback(|e: InputData| Msg::InputUpdate(e.value))
onkeydown=self.link.callback(|e: KeyboardEvent| {
placeholder={ctx.props().placeholder.clone()}
large={ctx.props().large}
disabled={ctx.props().disabled}
left_icon={ctx.props().left_icon}
left_element={ctx.props().left_element.clone()}
right_element={ctx.props().right_element.clone()}
value={self.input.clone()}
oninput={ctx.link().callback(|e: InputEvent| {
let value = e.target_unchecked_into::<HtmlInputElement>().value();
Msg::InputUpdate(value)
})}
onkeydown={ctx.link().callback(|e: KeyboardEvent| {
if e.key() == "ArrowUp" {
Msg::Up
} else if e.key() == "ArrowDown" {
@ -169,16 +178,16 @@ where
} else {
Msg::Noop
}
})
})}
/>
};
if buttons_on_the_left {
html! {
<ControlGroup
class=classes!("bp3-numeric-input")
fill=self.props.fill
large=self.props.large
class={classes!("bp3-numeric-input")}
fill={ctx.props().fill}
large={ctx.props().large}
>
{buttons}
{input_group}
@ -187,9 +196,9 @@ where
} else {
html! {
<ControlGroup
class=classes!("bp3-numeric-input")
fill=self.props.fill
large=self.props.large
class={classes!("bp3-numeric-input")}
fill={ctx.props().fill}
large={ctx.props().large}
>
{input_group}
{buttons}
@ -199,30 +208,6 @@ where
}
}
impl<T> NumericInput<T>
where
T: Add<Output = T>
+ Sub<Output = T>
+ Copy
+ Display
+ FromStr
+ PartialEq
+ PartialOrd
+ 'static,
{
fn update_value(&mut self, new_value: T) -> ShouldRender {
let new_value = self.props.bounds.clamp(new_value, self.props.increment);
if new_value != self.props.value {
self.input = new_value.to_string();
self.props.onchange.emit(new_value);
true
} else {
false
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct NumericInputRangeBounds<T> {
pub start: Bound<T>,

View file

@ -1,11 +1,9 @@
use crate::{Button, IconName};
use std::borrow::Cow;
use gloo::timers::callback::Timeout;
use std::cell::RefCell;
use std::fmt;
use std::rc::Rc;
use std::time::Duration;
use yew::prelude::*;
use yew::services::timeout::{TimeoutService, TimeoutTask};
pub struct PanelBuilder<F: Fn(Option<Html>, I) -> O, I, O> {
title: Option<Html>,
@ -119,9 +117,11 @@ impl From<StateAction> for Classes {
}
pub struct PanelStack {
timeout_task: Option<TimeoutTask>,
props: PanelStackProps,
link: ComponentLink<Self>,
timeout_task: Option<Timeout>,
// We keep track of the latest action to perform from the PanelStackState
// because we need a mutable access to the action.
action_to_perform: Option<StateAction>,
}
#[derive(Debug, Clone, PartialEq, Properties)]
@ -141,47 +141,36 @@ impl Component for PanelStack {
type Message = PanelStackMessage;
type Properties = PanelStackProps;
fn create(props: Self::Properties, link: ComponentLink<Self>) -> Self {
fn create(_ctx: &Context<Self>) -> Self {
Self {
timeout_task: None,
props,
link,
action_to_perform: None,
}
}
fn update(&mut self, msg: Self::Message) -> ShouldRender {
fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
match msg {
PanelStackMessage::PopPanel => {
self.props.state.opened_panels.borrow_mut().pop();
ctx.props().state.opened_panels.borrow_mut().pop();
true
}
}
}
fn change(&mut self, props: Self::Properties) -> ShouldRender {
if self.props != props {
self.props = props;
true
} else {
false
}
}
fn view(&self) -> Html {
let opened_panels = self.props.state.opened_panels.borrow();
let action = self.props.state.action;
let last = match action {
fn view(&self, ctx: &Context<Self>) -> Html {
let opened_panels = ctx.props().state.opened_panels.borrow();
let last = match self.action_to_perform {
Some(StateAction::Pop) => opened_panels.len() - 2,
_ => opened_panels.len() - 1,
};
html! {
<div
class=classes!(
class={classes!(
"bp3-panel-stack2",
action,
self.props.class.clone(),
)
self.action_to_perform,
ctx.props().class.clone(),
)}
>
{
opened_panels
@ -190,9 +179,9 @@ impl Component for PanelStack {
.rev()
.map(|(i, (title, content))| html! {
<Panel
title=title.clone()
title={title.clone()}
animation={
match action {
match self.action_to_perform {
_ if i == last => Animation::EnterStart,
Some(StateAction::Push) if i == last - 1 =>
Animation::ExitStart,
@ -201,8 +190,8 @@ impl Component for PanelStack {
_ => Animation::Exited,
}
}
onclose=(i > 0).then(|| self.props.onclose.clone()).flatten()
key=i
onclose={(i > 0).then(|| ctx.props().onclose.clone()).flatten()}
key={i}
>
// TODO the state of content doesn't seem to be kept when re-opening
// a panel using the same components
@ -215,21 +204,24 @@ impl Component for PanelStack {
}
}
fn rendered(&mut self, _first_render: bool) {
if self.props.state.action.take() == Some(StateAction::Pop) {
self.timeout_task.replace(TimeoutService::spawn(
Duration::from_millis(400),
self.link.callback(|_| PanelStackMessage::PopPanel),
));
fn changed(&mut self, ctx: &Context<Self>) -> bool {
self.action_to_perform = ctx.props().state.action;
true
}
fn rendered(&mut self, ctx: &Context<Self>, _first_render: bool) {
if self.action_to_perform.take() == Some(StateAction::Pop) {
let link = ctx.link().clone();
self.timeout_task.replace(Timeout::new(400, move || {
link.send_message(PanelStackMessage::PopPanel)
}));
}
}
}
struct Panel {
animation: Animation,
timeout_task: Option<TimeoutTask>,
props: PanelProps,
link: ComponentLink<Self>,
timeout_task: Option<Timeout>,
}
#[derive(Debug, Clone, PartialEq, Properties)]
@ -249,16 +241,14 @@ impl Component for Panel {
type Message = PanelMessage;
type Properties = PanelProps;
fn create(props: Self::Properties, link: ComponentLink<Self>) -> Self {
fn create(ctx: &Context<Self>) -> Self {
Self {
animation: props.animation,
animation: ctx.props().animation,
timeout_task: None,
props,
link,
}
}
fn update(&mut self, msg: Self::Message) -> ShouldRender {
fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {
match msg {
PanelMessage::UpdateAnimation(animation) => {
self.animation = animation;
@ -267,17 +257,7 @@ impl Component for Panel {
}
}
fn change(&mut self, props: Self::Properties) -> ShouldRender {
if self.props != props {
self.animation = props.animation;
self.props = props;
true
} else {
false
}
}
fn view(&self) -> Html {
fn view(&self, ctx: &Context<Self>) -> Html {
let style = if self.animation == Animation::Exited {
"display:none"
} else {
@ -294,15 +274,15 @@ impl Component for Panel {
Animation::Exited => None,
}
);
let back_button = self.props.onclose.clone().map(|onclose| {
let back_button = ctx.props().onclose.clone().map(|onclose| {
html! {
<Button
class=classes!("bp3-panel-stack-header-back")
style=Cow::Borrowed("padding-right:0")
icon=IconName::ChevronLeft
minimal=true
small=true
onclick=onclose.reform(|_| ())
class={classes!("bp3-panel-stack-header-back")}
style={"padding-right:0"}
icon={IconName::ChevronLeft}
minimal={true}
small={true}
onclick={onclose.reform(|_| ())}
>
// TODO: I get a lot of "VComp is not mounted" if I try to use the title
// of the previous panel
@ -311,47 +291,48 @@ impl Component for Panel {
});
html! {
<div class=classes style=style>
<div class={classes} style={style}>
<div class="bp3-panel-stack-header">
<span>{back_button.unwrap_or_default()}</span>
{self.props.title.clone().unwrap_or_default()}
{ctx.props().title.clone().unwrap_or_default()}
<span/>
</div>
{for self.props.children.iter()}
{for ctx.props().children.iter()}
</div>
}
}
fn rendered(&mut self, _first_render: bool) {
fn changed(&mut self, ctx: &Context<Self>) -> bool {
self.animation = ctx.props().animation;
true
}
fn rendered(&mut self, ctx: &Context<Self>, _first_render: bool) {
match self.animation {
Animation::EnterStart => {
self.timeout_task.replace(TimeoutService::spawn(
Duration::from_millis(0),
self.link
.callback(|_| PanelMessage::UpdateAnimation(Animation::Entering)),
));
let link = ctx.link().clone();
self.timeout_task.replace(Timeout::new(0, move || {
link.send_message(PanelMessage::UpdateAnimation(Animation::Entering));
}));
}
Animation::Entering => {
self.timeout_task.replace(TimeoutService::spawn(
Duration::from_millis(400),
self.link
.callback(|_| PanelMessage::UpdateAnimation(Animation::Entered)),
));
let link = ctx.link().clone();
self.timeout_task.replace(Timeout::new(400, move || {
link.send_message(PanelMessage::UpdateAnimation(Animation::Entered));
}));
}
Animation::Entered => {}
Animation::ExitStart => {
self.timeout_task.replace(TimeoutService::spawn(
Duration::from_millis(0),
self.link
.callback(|_| PanelMessage::UpdateAnimation(Animation::Exiting)),
));
let link = ctx.link().clone();
self.timeout_task.replace(Timeout::new(0, move || {
link.send_message(PanelMessage::UpdateAnimation(Animation::Exiting));
}));
}
Animation::Exiting => {
self.timeout_task.replace(TimeoutService::spawn(
Duration::from_millis(400),
self.link
.callback(|_| PanelMessage::UpdateAnimation(Animation::Exited)),
));
let link = ctx.link().clone();
self.timeout_task.replace(Timeout::new(400, move || {
link.send_message(PanelMessage::UpdateAnimation(Animation::Exited));
}));
}
Animation::Exited => {}
}

View file

@ -1,10 +1,6 @@
use crate::Intent;
use yew::prelude::*;
pub struct ProgressBar {
props: ProgressBarProps,
}
#[derive(Clone, PartialEq, Properties)]
pub struct ProgressBarProps {
#[prop_or_default]
@ -19,29 +15,9 @@ pub struct ProgressBarProps {
pub class: Classes,
}
impl Component for ProgressBar {
type Message = ();
type Properties = ProgressBarProps;
fn create(props: Self::Properties, _link: ComponentLink<Self>) -> Self {
Self { 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 width = if let Some(value) = self.props.value {
#[function_component(ProgressBar)]
pub fn progress_bar(props: &ProgressBarProps) -> Html {
let width = if let Some(value) = props.value {
// NOTE: nightly, issue #44095 for f32::clamp
// let percent = ((1000. * value).ceil() / 10.).clamp(0.,100.);
let percent = ((1000. * value).ceil() / 10.).max(0.).min(100.);
@ -51,16 +27,15 @@ impl Component for ProgressBar {
};
html! {
<div
class=classes!(
class={classes!(
"bp3-progress-bar",
self.props.intent,
(!self.props.animate).then(|| "bp3-no-animation"),
(!self.props.stripes).then(|| "bp3-no-stripes"),
self.props.class.clone(),
)
props.intent,
(!props.animate).then(|| "bp3-no-animation"),
(!props.stripes).then(|| "bp3-no-stripes"),
props.class.clone(),
)}
>
<div class=classes!("bp3-progress-meter") style={{width}}/>
<div class={classes!("bp3-progress-meter")} style={{width}}/>
</div>
}
}
}

View file

@ -1,9 +1,5 @@
use yew::prelude::*;
pub struct Radio {
props: RadioProps,
}
#[derive(Clone, PartialEq, Properties)]
pub struct RadioProps {
#[prop_or_default]
@ -17,59 +13,38 @@ pub struct RadioProps {
#[prop_or_default]
pub name: Option<String>,
#[prop_or_default]
pub onchange: Option<Callback<ChangeData>>,
pub onchange: Option<Callback<Event>>,
#[prop_or_default]
pub label: yew::virtual_dom::VNode,
#[prop_or_default]
pub value: Option<String>,
}
impl Component for Radio {
type Message = ();
type Properties = RadioProps;
fn create(props: Self::Properties, _link: ComponentLink<Self>) -> Self {
Self { 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 {
#[function_component(Radio)]
pub fn radio(props: &RadioProps) -> Html {
html! {
<label
class=classes!(
class={classes!(
"bp3-control",
"bp3-radio",
self.props.disabled.then(|| "bp3-disabled"),
self.props.inline.then(|| "bp3-inline"),
self.props.large.then(|| "bp3-large"),
)
props.disabled.then(|| "bp3-disabled"),
props.inline.then(|| "bp3-inline"),
props.large.then(|| "bp3-large"),
)}
>
<input
type="radio"
onchange={self.props.onchange.clone().unwrap_or_default()}
disabled=self.props.disabled
value={self.props.value.clone().unwrap_or_default()}
checked=self.props.checked.unwrap_or(false)
name={self.props.name.clone().unwrap_or_default()}
onchange={props.onchange.clone().unwrap_or_default()}
disabled={props.disabled}
value={props.value.clone().unwrap_or_default()}
checked={props.checked.unwrap_or(false)}
name={props.name.clone().unwrap_or_default()}
/>
<span
class=classes!("bp3-control-indicator")
class={classes!("bp3-control-indicator")}
>
</span>
{self.props.label.clone()}
{props.label.clone()}
</label>
}
}
}

View file

@ -1,10 +1,6 @@
use crate::Radio;
use yew::prelude::*;
pub struct RadioGroup<T: Clone + PartialEq + 'static> {
props: RadioGroupProps<T>,
}
#[derive(Clone, PartialEq, Properties)]
pub struct RadioGroupProps<T: Clone + PartialEq + 'static> {
#[prop_or_default]
@ -24,50 +20,26 @@ pub struct RadioGroupProps<T: Clone + PartialEq + 'static> {
pub class: Classes,
}
impl<T: Clone + PartialEq + 'static> Component for RadioGroup<T> {
type Message = ();
type Properties = RadioGroupProps<T>;
// impl<T: Clone + PartialEq + 'static> Component for RadioGroup<T> {
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 {
let option_children = self
.props
#[function_component(RadioGroup)]
pub fn radio_group<T: Clone + PartialEq + 'static>(props: &RadioGroupProps<T>) -> Html {
let option_children = props
.options
.iter()
.map(|(value, label)| {
let checked = self
.props
.value
.as_ref()
.map(|x| value == x)
.unwrap_or_default();
let checked = props.value.as_ref().map(|x| value == x).unwrap_or_default();
let value = value.clone();
html! {
<Radio
value="".to_string()
label=html!(label)
checked=checked
onchange=self.props.onchange.reform(move |_| value.clone())
inline=self.props.inline
disabled=self.props.disabled
large=self.props.large
value={"".to_string()}
label={html!(label)}
checked={checked}
onchange={props.onchange.reform(move |_| value.clone())}
inline={props.inline}
disabled={props.disabled}
large={props.large}
/>
}
})
@ -75,13 +47,13 @@ impl<T: Clone + PartialEq + 'static> Component for RadioGroup<T> {
html! {
<div
class=classes!(
class={classes!(
"bp3-radio-group",
self.props.class.clone(),
)
props.class.clone(),
)}
>
{
if let Some(label) = self.props.label.clone() {
if let Some(label) = props.label.clone() {
label
} else {
html!()
@ -91,4 +63,3 @@ impl<T: Clone + PartialEq + 'static> Component for RadioGroup<T> {
</div>
}
}
}

View file

@ -1,19 +1,19 @@
use crate::Intent;
use std::borrow::Cow;
use std::marker::PhantomData;
use wasm_bindgen::closure::Closure;
use wasm_bindgen::JsCast;
use web_sys::Element;
use yew::prelude::*;
pub struct Slider<T: Clone + PartialEq + 'static> {
props: SliderProps<T>,
mouse_move: Closure<dyn FnMut(MouseEvent)>,
mouse_up: Closure<dyn FnMut(MouseEvent)>,
link: ComponentLink<Self>,
handle_ref: NodeRef,
track_ref: NodeRef,
is_moving: bool,
focus_handle: bool,
phantom: PhantomData<T>,
}
#[derive(Clone, PartialEq, Properties)]
@ -42,35 +42,34 @@ impl<T: Clone + PartialEq + 'static> Component for Slider<T> {
type Message = Msg;
type Properties = SliderProps<T>;
fn create(props: Self::Properties, link: ComponentLink<Self>) -> Self {
fn create(ctx: &Context<Self>) -> Self {
let mouse_move = {
let link = link.clone();
let link = ctx.link().clone();
Closure::wrap(Box::new(move |event: web_sys::MouseEvent| {
link.send_message(Msg::Mouse(event));
}) as Box<dyn FnMut(_)>)
};
let mouse_up = {
let link = link.clone();
let link = ctx.link().clone();
Closure::wrap(Box::new(move |_event: web_sys::MouseEvent| {
link.send_message(Msg::StopChange);
}) as Box<dyn FnMut(_)>)
};
Self {
props,
mouse_move,
mouse_up,
link,
handle_ref: NodeRef::default(),
track_ref: NodeRef::default(),
is_moving: false,
focus_handle: false,
phantom: PhantomData,
}
}
fn update(&mut self, msg: Self::Message) -> ShouldRender {
fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
match msg {
Msg::StartChange if self.props.values.len() > 1 => {
let document = yew::utils::document();
Msg::StartChange if ctx.props().values.len() > 1 => {
let document = gloo::utils::document();
let event_target: &web_sys::EventTarget = document.as_ref();
self.is_moving = true;
event_target
@ -89,23 +88,23 @@ impl<T: Clone + PartialEq + 'static> Component for Slider<T> {
true
}
Msg::StartChange => false,
Msg::Mouse(event) if self.props.values.len() > 1 => {
Msg::Mouse(event) if ctx.props().values.len() > 1 => {
if event.buttons() == crate::MOUSE_EVENT_BUTTONS_PRIMARY {
let track_rect = self.track_ref.cast::<Element>().expect("no track ref");
let tick_size = (track_rect.client_width() as f64)
/ self.props.values.len().saturating_sub(1) as f64;
/ ctx.props().values.len().saturating_sub(1) as f64;
let pixel_delta =
(event.client_x() as f64) - track_rect.get_bounding_client_rect().left();
let position = (pixel_delta / tick_size).round() as usize;
let (value, _) =
self.props.values.get(position).unwrap_or_else(|| {
self.props.values.last().expect("No value in the vec")
ctx.props().values.get(position).unwrap_or_else(|| {
ctx.props().values.last().expect("No value in the vec")
});
if Some(value) != self.props.selected.as_ref() {
self.props.onchange.emit(value.clone());
if Some(value) != ctx.props().selected.as_ref() {
ctx.props().onchange.emit(value.clone());
}
true
@ -115,7 +114,7 @@ impl<T: Clone + PartialEq + 'static> Component for Slider<T> {
}
Msg::Mouse(_) => false,
Msg::StopChange => {
let document = yew::utils::document();
let document = gloo::utils::document();
let event_target: &web_sys::EventTarget = document.as_ref();
self.is_moving = false;
event_target
@ -132,43 +131,43 @@ impl<T: Clone + PartialEq + 'static> Component for Slider<T> {
.expect("No event listener to remove");
true
}
Msg::Keyboard(event) if self.props.values.len() > 1 => match event.key().as_str() {
Msg::Keyboard(event) if ctx.props().values.len() > 1 => match event.key().as_str() {
"ArrowDown" | "ArrowLeft" => {
self.focus_handle = true;
event.prevent_default();
let index = self
.props
let index = ctx
.props()
.values
.iter()
.position(|(value, _)| Some(value) == self.props.selected.as_ref())
.position(|(value, _)| Some(value) == ctx.props().selected.as_ref())
.map(|i| i.saturating_sub(1))
.unwrap_or(0);
let (value, _) = self.props.values[index].clone();
self.props.onchange.emit(value);
let (value, _) = ctx.props().values[index].clone();
ctx.props().onchange.emit(value);
true
}
"ArrowUp" | "ArrowRight" => {
self.focus_handle = true;
event.prevent_default();
let index = self
.props
let index = ctx
.props()
.values
.iter()
.position(|(value, _)| Some(value) == self.props.selected.as_ref())
.position(|(value, _)| Some(value) == ctx.props().selected.as_ref())
.map(|i| i.saturating_add(1))
.unwrap_or(0);
let (value, _) = self
.props
let (value, _) = ctx
.props()
.values
.get(index)
.unwrap_or_else(|| {
self.props.values.last().expect(
ctx.props().values.last().expect(
"Already check, \
there are at least 2 values in self.props.options; qed",
there are at least 2 values in ctx.props().options; qed",
)
})
.clone();
self.props.onchange.emit(value);
ctx.props().onchange.emit(value);
true
}
_ => false,
@ -177,23 +176,14 @@ impl<T: Clone + PartialEq + 'static> Component for Slider<T> {
}
}
fn change(&mut self, props: Self::Properties) -> ShouldRender {
if self.props != props {
self.props = props;
true
} else {
false
}
}
fn view(&self) -> Html {
let value_index = self
.props
fn view(&self, ctx: &Context<Self>) -> Html {
let value_index = ctx
.props()
.values
.iter()
.position(|(value, _)| Some(value) == self.props.selected.as_ref());
let labels = if self.props.values.len() > 1 {
self.props
.position(|(value, _)| Some(value) == ctx.props().selected.as_ref());
let labels = if ctx.props().values.len() > 1 {
ctx.props()
.values
.iter()
.enumerate()
@ -201,11 +191,11 @@ impl<T: Clone + PartialEq + 'static> Component for Slider<T> {
label.clone().map(|x| {
html! {
<div
class=classes!("bp3-slider-label")
style=format!(
class={classes!("bp3-slider-label")}
style={format!(
"left: {}%;", (i as f64) * 100.0
/ ((self.props.values.len() as f64) - 1.0)
)
/ ((ctx.props().values.len() as f64) - 1.0)
)}
>
{x}
</div>
@ -213,11 +203,11 @@ impl<T: Clone + PartialEq + 'static> Component for Slider<T> {
})
})
.collect::<Html>()
} else if let Some((_, Some(label))) = self.props.values.first() {
} else if let Some((_, Some(label))) = ctx.props().values.first() {
html! {
<div
class=classes!("bp3-slider-label")
style="left: 50%;"
class={classes!("bp3-slider-label")}
style={"left: 50%;"}
>
{label}
</div>
@ -225,9 +215,9 @@ impl<T: Clone + PartialEq + 'static> Component for Slider<T> {
} else {
html!()
};
let value_label = self.props.value_label.clone().map(|x| {
let value_label = ctx.props().value_label.clone().map(|x| {
html! {
<span class=classes!("bp3-slider-label")>
<span class={classes!("bp3-slider-label")}>
{x}
</span>
}
@ -235,12 +225,12 @@ impl<T: Clone + PartialEq + 'static> Component for Slider<T> {
html! {
<div
class=classes!(
class={classes!(
"bp3-slider",
self.props.vertical.then(|| "bp3-vertical"),
)
onmousedown=(self.props.values.len() > 1).then(
|| self.link.batch_callback(
ctx.props().vertical.then(|| "bp3-vertical"),
)}
onmousedown={(ctx.props().values.len() > 1).then(
|| ctx.link().batch_callback(
|event: MouseEvent| {
if event.buttons() ==
crate::MOUSE_EVENT_BUTTONS_PRIMARY
@ -251,19 +241,19 @@ impl<T: Clone + PartialEq + 'static> Component for Slider<T> {
}
}
)
)
)}
>
<div
class=classes!("bp3-slider-track")
class={classes!("bp3-slider-track")}
ref={self.track_ref.clone()}
>
{
if value_index.is_none() && !self.props.values.is_empty() {
if value_index.is_none() && !ctx.props().values.is_empty() {
html! {
<div
class=classes!("bp3-slider-progress")
class={classes!("bp3-slider-progress")}
style="top: 0px;"
onkeydown=self.link.callback(|event| Msg::Keyboard(event))
onkeydown={ctx.link().callback(|event| Msg::Keyboard(event))}
tabindex=0
>
</div>
@ -271,7 +261,7 @@ impl<T: Clone + PartialEq + 'static> Component for Slider<T> {
} else {
html! {
<div
class=classes!("bp3-slider-progress")
class={classes!("bp3-slider-progress")}
style="top: 0px;"
>
</div>
@ -280,18 +270,18 @@ impl<T: Clone + PartialEq + 'static> Component for Slider<T> {
}
{
match value_index {
Some(index) if self.props.values.len() > 1
&& self.props.intent.is_some() => {
Some(index) if ctx.props().values.len() > 1
&& ctx.props().intent.is_some() => {
html! {
<div
class=classes!("bp3-slider-progress", self.props.intent)
style=format!(
class={classes!("bp3-slider-progress", ctx.props().intent)}
style={format!(
"left: 0%; right: {}%; top: 0px;",
100.0 - (
100.0 * (index as f64)
/ (self.props.values.len() as f64 - 1.0)
)
/ (ctx.props().values.len() as f64 - 1.0)
)
)}
>
</div>
}
@ -300,34 +290,34 @@ impl<T: Clone + PartialEq + 'static> Component for Slider<T> {
}
}
</div>
<div class=classes!("bp3-slider-axis")>
<div class={classes!("bp3-slider-axis")}>
{labels}
</div>
{
match value_index {
Some(index) if self.props.values.len() > 1 =>
Some(index) if ctx.props().values.len() > 1 =>
{
html! {
<span
class=classes!(
class={classes!(
"bp3-slider-handle",
self.is_moving.then(|| "bp3-active"),
)
)}
ref={self.handle_ref.clone()}
style=format!(
style={format!(
"left: calc({}% - 8px);",
100.0 * (index as f64)
/ (self.props.values.len() as f64 - 1.0),
)
onmousedown=self.link.batch_callback(
/ (ctx.props().values.len() as f64 - 1.0),
)}
onmousedown={ctx.link().batch_callback(
|event: MouseEvent| {
if event.buttons() == crate::MOUSE_EVENT_BUTTONS_PRIMARY {
vec![Msg::StartChange]
} else {
vec![]
}
})
onkeydown=self.link.callback(|event| Msg::Keyboard(event))
})}
onkeydown={ctx.link().callback(|event| Msg::Keyboard(event))}
tabindex=0
>
{value_label.clone().unwrap_or_default()}
@ -337,10 +327,10 @@ impl<T: Clone + PartialEq + 'static> Component for Slider<T> {
Some(_) => {
html! {
<span
class=classes!(
class={classes!(
"bp3-slider-handle",
self.is_moving.then(|| "bp3-active"),
)
)}
ref={self.handle_ref.clone()}
style="left: calc(50% - 8px);"
>
@ -355,7 +345,7 @@ impl<T: Clone + PartialEq + 'static> Component for Slider<T> {
}
}
fn rendered(&mut self, _: bool) {
fn rendered(&mut self, _ctx: &Context<Self>, _: bool) {
if self.focus_handle {
if let Some(element) = self.handle_ref.cast::<web_sys::HtmlElement>() {
let _ = element.focus();

View file

@ -10,10 +10,6 @@ pub const SPINNER_SIZE_SMALL: f32 = 20.0;
pub const SPINNER_SIZE_STANDARD: f32 = 50.0;
pub const SPINNER_SIZE_LARGE: f32 = 100.0;
pub struct Spinner {
props: SpinnerProps,
}
#[derive(Clone, PartialEq, Properties)]
pub struct SpinnerProps {
#[prop_or_default]
@ -26,29 +22,9 @@ pub struct SpinnerProps {
pub value: f32,
}
impl Component for Spinner {
type Message = ();
type Properties = SpinnerProps;
fn create(props: Self::Properties, _link: ComponentLink<Self>) -> Self {
Spinner { 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 size = f32::max(SPINNER_MIN_SIZE, self.props.size);
#[function_component(Spinner)]
pub fn spinner(props: &SpinnerProps) -> Html {
let size = f32::max(SPINNER_MIN_SIZE, props.size);
let stroke_width = f32::min(MIN_STROKE_WIDTH, (STROKE_WIDTH * SPINNER_SIZE_LARGE) / size);
let view_box = {
let radius = R + stroke_width / 2.00;
@ -64,39 +40,38 @@ impl Component for Spinner {
R = R,
R2 = R * 2.0,
);
let stroke_offset = PATH_LENGTH - PATH_LENGTH * self.props.value.clamp(0.0, 1.0);
let stroke_offset = PATH_LENGTH - PATH_LENGTH * props.value.clamp(0.0, 1.0);
html! {
<div
class=classes!(
class={classes!(
"bp3-spinner",
self.props.intent,
self.props.class.clone(),
)
props.intent,
props.class.clone(),
)}
>
<div
class=classes!("bp3-spinner-animation")
class={classes!("bp3-spinner-animation")}
>
<svg
width=size.to_string()
height=size.to_string()
stroke-width=stroke_width.to_string()
viewBox=view_box
width={size.to_string()}
height={size.to_string()}
stroke-width={stroke_width.to_string()}
viewBox={view_box}
>
<path
class=classes!("bp3-spinner-track")
d=spinner_track.clone()
class={classes!("bp3-spinner-track")}
d={spinner_track.clone()}
/>
<path
class=classes!("bp3-spinner-head")
d=spinner_track
pathLength=PATH_LENGTH.to_string()
stroke-dasharray=format!("{} {}", PATH_LENGTH, PATH_LENGTH)
stroke-dashoffset=stroke_offset.to_string()
class={classes!("bp3-spinner-head")}
d={spinner_track}
pathLength={PATH_LENGTH.to_string()}
stroke-dasharray={format!("{} {}", PATH_LENGTH, PATH_LENGTH)}
stroke-dashoffset={stroke_offset.to_string()}
/>
</svg>
</div>
</div>
}
}
}

View file

@ -1,9 +1,5 @@
use yew::prelude::*;
pub struct Switch {
props: SwitchProps,
}
#[derive(Clone, PartialEq, Properties)]
pub struct SwitchProps {
#[prop_or_default]
@ -28,36 +24,16 @@ pub struct SwitchProps {
pub align_right: bool,
}
impl Component for Switch {
type Message = ();
type Properties = SwitchProps;
fn create(props: Self::Properties, _link: ComponentLink<Self>) -> Self {
Self { 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 {
#[function_component(Switch)]
pub fn switch(props: &SwitchProps) -> Html {
let display_label = {
if self.props.inner_label.is_some() || self.props.inner_label_checked.is_some() {
let inner_label = self.props.inner_label.as_deref().unwrap_or_default();
let inner_label_checked = self.props.inner_label_checked.as_ref();
if props.inner_label.is_some() || props.inner_label_checked.is_some() {
let inner_label = props.inner_label.as_deref().unwrap_or_default();
let inner_label_checked = props.inner_label_checked.as_ref();
html! {
<>
<div class=classes!("bp3-control-indicator-child")>
<div class=classes!("bp3-switch-inner-text")>
<div class={classes!("bp3-control-indicator-child")}>
<div class={classes!("bp3-switch-inner-text")}>
{
if let Some(label_checked) = inner_label_checked {
label_checked.clone()
@ -67,8 +43,8 @@ impl Component for Switch {
}
</div>
</div>
<div class=classes!("bp3-control-indicator-child")>
<div class=classes!("bp3-switch-inner-text")>
<div class={classes!("bp3-control-indicator-child")}>
<div class={classes!("bp3-switch-inner-text")}>
{inner_label.to_string()}
</div>
</div>
@ -80,33 +56,32 @@ impl Component for Switch {
};
html! {
<label
class=classes!(
class={classes!(
"bp3-control",
"bp3-switch",
self.props.disabled.then(|| "bp3-disabled"),
self.props.inline.then(|| "bp3-inline"),
self.props.large.then(|| "bp3-large"),
self.props.class.clone(),
if self.props.align_right {
props.disabled.then(|| "bp3-disabled"),
props.inline.then(|| "bp3-inline"),
props.large.then(|| "bp3-large"),
props.class.clone(),
if props.align_right {
"bp3-align-right"
} else {
"bp3-align-left"
},
)
)}
>
<input
type="checkbox"
checked={self.props.checked}
onclick={self.props.onclick.clone()}
disabled=self.props.disabled
checked={props.checked}
onclick={props.onclick.clone()}
disabled={props.disabled}
/>
<span
class=classes!("bp3-control-indicator")
class={classes!("bp3-control-indicator")}
>
{display_label}
</span>
{self.props.label.clone()}
{props.label.clone()}
</label>
}
}
}

View file

@ -1,12 +1,13 @@
use std::collections::{hash_map::DefaultHasher, HashMap};
use std::hash::{Hash, Hasher};
use std::marker::PhantomData;
use web_sys::HtmlElement;
use yew::prelude::*;
pub struct Tabs<T: Clone + PartialEq + Hash + 'static> {
props: TabsProps<T>,
tab_refs: HashMap<u64, NodeRef>,
indicator_ref: NodeRef,
phantom: PhantomData<T>,
}
#[derive(Clone, PartialEq, Properties)]
@ -34,8 +35,9 @@ impl<T: Clone + PartialEq + Hash + 'static> Component for Tabs<T> {
type Message = ();
type Properties = TabsProps<T>;
fn create(props: Self::Properties, _link: ComponentLink<Self>) -> Self {
let tab_refs = props
fn create(ctx: &Context<Self>) -> Self {
let tab_refs = ctx
.props()
.tabs
.iter()
.map(|x| {
@ -46,62 +48,53 @@ impl<T: Clone + PartialEq + Hash + 'static> Component for Tabs<T> {
})
.collect::<HashMap<_, _>>();
Tabs {
props,
Self {
tab_refs,
indicator_ref: Default::default(),
phantom: PhantomData,
}
}
fn update(&mut self, _msg: Self::Message) -> ShouldRender {
fn update(&mut self, _ctx: &Context<Self>, _msg: Self::Message) -> bool {
true
}
fn change(&mut self, props: Self::Properties) -> ShouldRender {
if self.props != props {
self.props = props;
true
} else {
false
}
}
fn view(&self) -> Html {
let tabs = self
.props
fn view(&self, ctx: &Context<Self>) -> Html {
let tabs = ctx
.props()
.tabs
.iter()
.map(|x| {
let mut hasher = DefaultHasher::new();
x.id.hash(&mut hasher);
let id = hasher.finish();
let title_id = format!("bp3-tab-title_{}_{}", self.props.id, id);
let panel_id = format!("bp3-tab-panel_{}_{}", self.props.id, id);
let selected = self.props.selected_tab_id == x.id;
let title_id = format!("bp3-tab-title_{}_{}", ctx.props().id, id);
let panel_id = format!("bp3-tab-panel_{}_{}", ctx.props().id, id);
let selected = ctx.props().selected_tab_id == x.id;
(x, id, title_id, panel_id, selected)
})
.collect::<Vec<_>>();
html! {
<div
class=classes!(
class={classes!(
"bp3-tabs",
self.props.vertical.then(|| "bp3-vertical"),
self.props.class.clone(),
)
ctx.props().vertical.then(|| "bp3-vertical"),
ctx.props().class.clone(),
)}
>
<div
class=classes!(
class={classes!(
"bp3-tab-list",
self.props.large.then(|| "bp3-large"),
)
ctx.props().large.then(|| "bp3-large"),
)}
>
{
if self.props.animate {
if ctx.props().animate {
html! {
<div
class="bp3-tab-indicator-wrapper"
ref=self.indicator_ref.clone()
ref={self.indicator_ref.clone()}
>
<div class="bp3-tab-indicator" />
</div>
@ -115,27 +108,27 @@ impl<T: Clone + PartialEq + Hash + 'static> Component for Tabs<T> {
.iter()
.map(|(props, id, title_id, panel_id, selected)| html! {
<div
class=classes!(
class={classes!(
"bp3-tab",
props.title_class.clone(),
)
aria-disabled=props.disabled.then(|| "true")
aria-expanded=selected.to_string()
aria-selected=selected.to_string()
)}
aria-disabled={props.disabled.then(|| "true")}
aria-expanded={selected.to_string()}
aria-selected={selected.to_string()}
role="tab"
tabIndex={(!props.disabled).then(|| "0")}
id=title_id.to_string()
aria-controls=panel_id.to_string()
data-tab-id=id.to_string()
id={title_id.to_string()}
aria-controls={panel_id.to_string()}
data-tab-id={id.to_string()}
onclick={(!props.disabled).then(|| {
let tab_id = props.id.clone();
self
.props
ctx
.props()
.onchange
.reform(move |_| tab_id.clone())
})}
key=*id
ref=self.tab_refs[id].clone()
key={*id}
ref={self.tab_refs[id].clone()}
>
{ props.title.clone() }
</div>
@ -147,19 +140,19 @@ impl<T: Clone + PartialEq + Hash + 'static> Component for Tabs<T> {
tabs
.iter()
.filter(|(_, _, _, _, selected)| {
!self.props.render_active_panel_only || *selected
!ctx.props().render_active_panel_only || *selected
})
.map(|(props, id, title_id, panel_id, selected)| html! {
<div
class=classes!(
class={classes!(
"bp3-tab-panel",
selected.then(|| props.panel_class.clone()),
)
aria-labelledby=title_id.to_string()
aria-hidden=(!selected).then(|| "true")
)}
aria-labelledby={title_id.to_string()}
aria-hidden={(!selected).then(|| "true")}
role="tabpanel"
id=panel_id.to_string()
key=*id
id={panel_id.to_string()}
key={*id}
>
{ props.panel.clone() }
</div>
@ -170,10 +163,10 @@ impl<T: Clone + PartialEq + Hash + 'static> Component for Tabs<T> {
}
}
fn rendered(&mut self, _first_render: bool) {
if self.props.animate {
fn rendered(&mut self, ctx: &Context<Self>, _first_render: bool) {
if ctx.props().animate {
let mut hasher = DefaultHasher::new();
self.props.selected_tab_id.hash(&mut hasher);
ctx.props().selected_tab_id.hash(&mut hasher);
let id = hasher.finish();
let indicator = self.indicator_ref.cast::<HtmlElement>().unwrap();

View file

@ -1,11 +1,6 @@
use crate::{if_html, Icon, IconName, Intent, Text};
use std::borrow::Cow;
use yew::prelude::*;
pub struct Tag {
props: TagProps,
}
#[derive(Clone, PartialEq, Properties)]
pub struct TagProps {
#[prop_or_default]
@ -36,81 +31,60 @@ pub struct TagProps {
#[prop_or_default]
pub round: bool,
#[prop_or_default]
pub title: Option<Cow<'static, str>>,
pub title: Option<String>,
#[prop_or_default]
pub class: Classes,
#[prop_or_default]
pub style: Option<Cow<'static, str>>,
pub style: Option<String>,
}
impl Component for Tag {
type Message = ();
type Properties = TagProps;
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 />);
#[function_component(Tag)]
pub fn tag(props: &TagProps) -> Html {
let icon = if_html!(let Some(icon) = props.icon => <Icon icon={icon} />);
let right_icon =
if_html!(let Some(right_icon) = self.props.right_icon => <Icon icon=right_icon />);
if_html!(let Some(right_icon) = props.right_icon => <Icon icon={right_icon} />);
let remove_button = if_html! {
let Some(callback) = self.props.onremove.clone() =>
let Some(callback) = props.onremove.clone() =>
html!(
<button
class=classes!("bp3-tag-remove")
class={classes!("bp3-tag-remove")}
onclick={callback}
tabindex={self.props.interactive.then(|| "0")}
tabindex={props.interactive.then(|| "0")}
>
<Icon icon=IconName::SmallCross />
<Icon icon={IconName::SmallCross} />
</button>
)
};
html! {
<span
class=classes!(
class={classes!(
"bp3-tag",
self.props.intent,
self.props.active.then(|| "bp3-active"),
self.props.fill.then(|| "bp3-fill"),
self.props.interactive.then(|| "bp3-interactive"),
self.props.large.then(|| "bp3-large"),
self.props.minimal.then(|| "bp3-minimal"),
self.props.round.then(|| "bp3-round"),
self.props.class.clone(),
)
style=self.props.style.clone()
onclick={self.props.onclick.clone()}
props.intent,
props.active.then(|| "bp3-active"),
props.fill.then(|| "bp3-fill"),
props.interactive.then(|| "bp3-interactive"),
props.large.then(|| "bp3-large"),
props.minimal.then(|| "bp3-minimal"),
props.round.then(|| "bp3-round"),
props.class.clone(),
)}
style={props.style.clone()}
onclick={props.onclick.clone()}
>
{icon}
<Text
class=classes!("bp3-fill")
ellipsize={!self.props.multiline}
title=self.props.title.clone()
class={classes!("bp3-fill")}
ellipsize={!props.multiline}
title={props.title.clone()}
inline=true
>
{self.props.children.clone()}
{props.children.clone()}
</Text>
{right_icon}
{remove_button}
</span>
}
}
}

View file

@ -1,10 +1,5 @@
use std::borrow::Cow;
use yew::prelude::*;
pub struct Text {
props: TextProps,
}
#[derive(Clone, PartialEq, Properties)]
pub struct TextProps {
#[prop_or_default]
@ -17,44 +12,23 @@ pub struct TextProps {
#[prop_or_default]
pub inline: bool,
#[prop_or_default]
pub title: Option<Cow<'static, str>>,
pub title: Option<String>,
#[prop_or_default]
pub style: Option<Cow<'static, str>>,
pub style: Option<String>,
}
impl Component for Text {
type Message = ();
type Properties = TextProps;
fn create(props: Self::Properties, _link: ComponentLink<Self>) -> Self {
Text { 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 {
#[function_component(Text)]
pub fn text(props: &TextProps) -> Html {
html! {
<@{if self.props.inline { "span" } else { "div"}}
class=classes!(
self.props.class.clone(),
self.props.ellipsize.then (|| "bp3-text-overflow-ellipsis"),
)
style=self.props.style.clone()
title=self.props.title.clone()
<@{if props.inline { "span" } else { "div"}}
class={classes!(
props.class.clone(),
props.ellipsize.then (|| "bp3-text-overflow-ellipsis"),
)}
style={props.style.clone()}
title={props.title.clone()}
>
{self.props.children.clone()}
{props.children.clone()}
</@>
}
}
}

View file

@ -1,10 +1,6 @@
use crate::Intent;
use yew::prelude::*;
pub struct TextArea {
props: TextAreaProps,
}
#[derive(Clone, PartialEq, Properties)]
pub struct TextAreaProps {
#[prop_or_default]
@ -22,44 +18,23 @@ pub struct TextAreaProps {
#[prop_or_default]
pub small: bool,
#[prop_or_default]
pub onchange: Option<Callback<ChangeData>>,
pub onchange: Option<Callback<Event>>,
}
impl Component for TextArea {
type Message = ();
type Properties = TextAreaProps;
fn create(props: Self::Properties, _link: ComponentLink<Self>) -> Self {
TextArea { 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 {
#[function_component(TextArea)]
pub fn text_area(props: &TextAreaProps) -> Html {
let classes = classes!(
"bp3-input",
self.props.intent,
self.props.class.clone(),
self.props.fill.then(|| "bp3-fill"),
self.props.small.then(|| "bp3-small"),
self.props.large.then(|| "bp3-large"),
props.intent,
props.class.clone(),
props.fill.then(|| "bp3-fill"),
props.small.then(|| "bp3-small"),
props.large.then(|| "bp3-large"),
);
html! {
<textarea
class=classes
onchange=self.props.onchange.clone()
class={classes}
onchange={props.onchange.clone()}
/>
}
}
}

View file

@ -6,6 +6,7 @@ use std::cell::{Ref, RefCell, RefMut};
use std::collections::hash_map::DefaultHasher;
use std::collections::HashMap;
use std::hash::{Hash, Hasher};
use std::marker::PhantomData;
use std::rc::Rc;
use yew::prelude::*;
@ -41,13 +42,13 @@ impl<T> From<id_tree::Tree<NodeData<T>>> for TreeData<T> {
}
}
pub struct Tree<T: Clone> {
props: TreeProps<T>,
pub struct Tree<T: Clone + PartialEq> {
previous_expanded_state: RefCell<HashMap<u64, bool>>,
phantom: PhantomData<T>,
}
#[derive(Clone, PartialEq, Properties)]
pub struct TreeProps<T: Clone> {
pub struct TreeProps<T: Clone + PartialEq> {
#[prop_or_default]
pub is_expanded: bool,
pub tree: TreeData<T>,
@ -112,41 +113,32 @@ impl<T: Clone + PartialEq + 'static> Component for Tree<T> {
type Message = ();
type Properties = TreeProps<T>;
fn create(props: Self::Properties, _link: ComponentLink<Self>) -> Self {
Tree {
props,
fn create(_ctx: &Context<Self>) -> Self {
Self {
previous_expanded_state: Default::default(),
phantom: PhantomData,
}
}
fn update(&mut self, _msg: Self::Message) -> ShouldRender {
fn update(&mut self, _ctx: &Context<Self>, _msg: Self::Message) -> bool {
true
}
fn change(&mut self, props: Self::Properties) -> ShouldRender {
if self.props != props {
self.props = props;
true
} else {
false
}
}
fn view(&self) -> Html {
let tree = self.props.tree.borrow();
fn view(&self, ctx: &Context<Self>) -> Html {
let tree = ctx.props().tree.borrow();
let nodes = if let Some(root_id) = tree.root_node_id() {
self.render_children(root_id, 0)
self.render_children(ctx, root_id, 0)
} else {
html!()
};
html! {
<div class=classes!(
<div class={classes!(
"bp3-tree",
self.props.class.clone(),
)>
<ul class=classes!("bp3-tree-node-list")>
ctx.props().class.clone(),
)}>
<ul class={classes!("bp3-tree-node-list")}>
{nodes}
</ul>
</div>
@ -154,9 +146,15 @@ impl<T: Clone + PartialEq + 'static> Component for Tree<T> {
}
}
impl<T: Clone> Tree<T> {
fn render_children(&self, node_id: &NodeId, depth: u32) -> yew::virtual_dom::VNode {
let tree = self.props.tree.borrow();
// FIXME: The 'static bound here is probably wrong. Fix this at the end of PR.
impl<T: 'static + Clone + PartialEq> Tree<T> {
fn render_children(
&self,
ctx: &Context<Self>,
node_id: &NodeId,
depth: u32,
) -> yew::virtual_dom::VNode {
let tree = ctx.props().tree.borrow();
let node = tree.get(node_id).unwrap();
let children = node.children();
@ -177,26 +175,26 @@ impl<T: Clone> Tree<T> {
let inner_nodes = if !data.is_expanded && !previous_is_expanded.unwrap_or(true) {
Default::default()
} else {
self.render_children(node_id, depth + 1)
self.render_children(ctx, node_id, depth + 1)
};
html! {
<TreeNode
disabled=data.disabled
has_caret=data.has_caret
icon=data.icon
icon_color=data.icon_color.clone()
icon_intent=data.icon_intent
is_expanded=data.is_expanded
is_selected=data.is_selected
label=data.label.clone()
secondary_label=data.secondary_label.clone()
on_collapse=self.props.on_collapse.clone()
on_expand=self.props.on_expand.clone()
onclick=self.props.onclick.clone()
depth=depth
node_id=node_id.clone()
key=key
disabled={data.disabled}
has_caret={data.has_caret}
icon={data.icon}
icon_color={data.icon_color.clone()}
icon_intent={data.icon_intent}
is_expanded={data.is_expanded}
is_selected={data.is_selected}
label={data.label.clone()}
secondary_label={data.secondary_label.clone()}
on_collapse={ctx.props().on_collapse.clone()}
on_expand={ctx.props().on_expand.clone()}
onclick={ctx.props().onclick.clone()}
depth={depth}
node_id={node_id.clone()}
key={key}
>
{inner_nodes}
</TreeNode>
@ -207,7 +205,6 @@ impl<T: Clone> Tree<T> {
}
struct TreeNode {
props: TreeNodeProps,
handler_caret_click: Callback<MouseEvent>,
handler_click: Callback<MouseEvent>,
}
@ -260,34 +257,33 @@ impl Component for TreeNode {
type Message = TreeNodeMessage;
type Properties = TreeNodeProps;
fn create(props: Self::Properties, link: ComponentLink<Self>) -> Self {
fn create(ctx: &Context<Self>) -> Self {
TreeNode {
handler_caret_click: link.callback(TreeNodeMessage::CaretClick),
handler_click: link.callback(TreeNodeMessage::Click),
props,
handler_caret_click: ctx.link().callback(TreeNodeMessage::CaretClick),
handler_click: ctx.link().callback(TreeNodeMessage::Click),
}
}
fn update(&mut self, msg: Self::Message) -> ShouldRender {
if self.props.disabled {
fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
if ctx.props().disabled {
return false;
}
match msg {
TreeNodeMessage::CaretClick(event) => {
if self.props.is_expanded {
if let Some(on_collapse) = self.props.on_collapse.as_ref() {
if ctx.props().is_expanded {
if let Some(on_collapse) = ctx.props().on_collapse.as_ref() {
event.stop_propagation();
on_collapse.emit((self.props.node_id.clone(), event));
on_collapse.emit((ctx.props().node_id.clone(), event));
}
} else if let Some(on_expand) = self.props.on_expand.as_ref() {
} else if let Some(on_expand) = ctx.props().on_expand.as_ref() {
event.stop_propagation();
on_expand.emit((self.props.node_id.clone(), event));
on_expand.emit((ctx.props().node_id.clone(), event));
}
}
TreeNodeMessage::Click(event) => {
if let Some(onclick) = self.props.onclick.as_ref() {
onclick.emit((self.props.node_id.clone(), event));
if let Some(onclick) = ctx.props().onclick.as_ref() {
onclick.emit((ctx.props().node_id.clone(), event));
}
}
}
@ -295,38 +291,23 @@ impl Component for TreeNode {
false
}
fn change(&mut self, props: Self::Properties) -> ShouldRender {
if self.props != props {
// crate::log!(
// "rerender {:?} {} {:?}",
// self.props.node_id,
// self.props.children == props.children,
// self.props.icon,
// );
self.props = props;
true
} else {
false
}
}
fn view(&self) -> Html {
let content_style = format!("padding-left: {}px;", 23 * self.props.depth);
fn view(&self, ctx: &Context<Self>) -> Html {
let content_style = format!("padding-left: {}px;", 23 * ctx.props().depth);
html! {
<li class=classes!(
<li class={classes!(
"bp3-tree-node",
self.props.is_selected.then(|| "bp3-tree-node-selected")
)>
ctx.props().is_selected.then(|| "bp3-tree-node-selected")
)}>
<div
class="bp3-tree-node-content"
style=content_style
onclick=self.handler_click.clone()
style={content_style}
onclick={self.handler_click.clone()}
>
{
if self.props.has_caret {
if ctx.props().has_caret {
let mut class = Classes::from("bp3-tree-node-caret");
class.push(if self.props.is_expanded {
class.push(if ctx.props().is_expanded {
"bp3-tree-node-caret-open"
} else {
"bp3-tree-node-caret-closed"
@ -334,9 +315,9 @@ impl Component for TreeNode {
html! {
<Icon
class=classes!(class.to_string())
icon=IconName::ChevronRight
onclick=self.handler_caret_click.clone()
class={classes!(class.to_string())}
icon={IconName::ChevronRight}
onclick={self.handler_caret_click.clone()}
/>
}
} else {
@ -346,16 +327,16 @@ impl Component for TreeNode {
}
}
<Icon
class=classes!("bp3-tree-node-icon")
icon=self.props.icon.unwrap_or_default()
color=self.props.icon_color.clone()
intent=self.props.icon_intent
class={classes!("bp3-tree-node-icon")}
icon={ctx.props().icon.unwrap_or_default()}
color={ctx.props().icon_color.clone()}
intent={ctx.props().icon_intent}
/>
<span class=classes!("bp3-tree-node-label")>{self.props.label.clone()}</span>
<span class={classes!("bp3-tree-node-label")}>{ctx.props().label.clone()}</span>
{
if let Some(label) = self.props.secondary_label.clone() {
if let Some(label) = ctx.props().secondary_label.clone() {
html!(
<span class=classes!("bp3-tree-node-secondary-label")>
<span class={classes!("bp3-tree-node-secondary-label")}>
{label}
</span>
)
@ -364,9 +345,9 @@ impl Component for TreeNode {
}
}
</div>
<Collapse is_open=self.props.is_expanded>
<ul class=classes!("bp3-tree-node-list")>
{self.props.children.clone()}
<Collapse is_open={ctx.props().is_expanded}>
<ul class={classes!("bp3-tree-node-list")}>
{ctx.props().children.clone()}
</ul>
</Collapse>
</li>

View file

@ -2,7 +2,7 @@
name = "yewprint-doc"
version = "0.1.0"
authors = ["Cecile Tonglet <cecile.tonglet@cecton.com>"]
edition = "2018"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@ -10,11 +10,14 @@ edition = "2018"
crate-type = ["cdylib"]
[dependencies]
wasm-bindgen = "=0.2"
yew = "0.18"
web-sys = { version = "0.3", features = ["Window", "MediaQueryList"] }
wasm-bindgen = "0.2"
web-sys = { version = "0.3", features = ["Window", "MediaQueryList", "Event", "HtmlInputElement"] }
gloo = "0.6"
yew = "0.19"
yew-router = "0.16"
# yew = { git = "https://github.com/yewstack/yew", branch = "master" }
# yew-router = { git = "https://github.com/yewstack/yew", branch = "master" }
yewprint = { path = ".." }
yew-router = "0.15"
# `wee_alloc` is a tiny allocator for wasm that is only ~1K in code size
# compared to the default allocator's ~10K. It is slower than the default

View file

@ -22,19 +22,21 @@ use crate::tag::*;
use crate::text::*;
use crate::text_area::*;
use crate::tree::*;
use std::borrow::Cow;
use yew::prelude::*;
use yew_router::{
agent::{RouteAgentDispatcher, RouteRequest},
router::Router,
Switch,
};
use yew_router::prelude::*;
use yewprint::{IconName, Menu, MenuItem};
#[function_component(AppRoot)]
pub fn app_root() -> Html {
html! {
<BrowserRouter>
<App></App>
</BrowserRouter>
}
}
pub struct App {
link: ComponentLink<Self>,
dark_theme: bool,
route_dispatcher: RouteAgentDispatcher,
}
pub enum Msg {
@ -46,33 +48,30 @@ impl Component for App {
type Message = Msg;
type Properties = ();
fn create(_: Self::Properties, link: ComponentLink<Self>) -> Self {
fn create(_ctx: &Context<Self>) -> Self {
App {
dark_theme: web_sys::window()
.and_then(|x| x.match_media("(prefers-color-scheme: dark)").ok().flatten())
.map(|x| x.matches())
.unwrap_or(true),
link,
route_dispatcher: RouteAgentDispatcher::new(),
}
}
fn update(&mut self, msg: Self::Message) -> ShouldRender {
fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
match msg {
Msg::ToggleLight => self.dark_theme ^= true,
Msg::GoToMenu(doc_menu) => {
self.route_dispatcher
.send(RouteRequest::ChangeRoute(doc_menu.into()));
if let Some(history) = ctx.link().history() {
history.push(doc_menu);
} else {
gloo::console::warn!("Could not get history from Context");
}
}
}
true
}
fn change(&mut self, _props: Self::Properties) -> ShouldRender {
true
}
fn view(&self) -> Html {
fn view(&self, ctx: &Context<Self>) -> Html {
let netlify_badge = if self.dark_theme {
"https://www.netlify.com/img/global/badges/netlify-color-accent.svg"
} else {
@ -89,21 +88,151 @@ impl Component for App {
IconName::Moon
};
html! {
<div class=classes!("docs-root", self.dark_theme.then(|| "bp3-dark"))>
<div class=classes!("docs-app")>
<div class=classes!("docs-nav-wrapper")>
<div class=classes!("docs-nav")>
<div class=classes!("docs-nav-title")>
<a class=classes!("docs-logo") href="/">
let menu = html! {
<Menu>
<MenuItem
text={html!(go_to_theme_label)}
onclick={ctx.link()
.callback(|_| Msg::ToggleLight)}
icon={go_to_theme_icon}
/>
<MenuItem
text={html!("Button")}
onclick={ctx.link()
.callback(|_| Msg::GoToMenu(DocMenu::Button))}
/>
<MenuItem
text={html!("ButtonGroup")}
onclick={ctx.link()
.callback(|_| Msg::GoToMenu(DocMenu::ButtonGroup))}
/>
<MenuItem
text={html!("Callout")}
onclick={ctx.link()
.callback(|_| Msg::GoToMenu(DocMenu::Callout))}
/>
<MenuItem
text={html!("Card")}
onclick={ctx.link()
.callback(|_| Msg::GoToMenu(DocMenu::Card))}
/>
<MenuItem
text={html!("Checkbox")}
onclick={ctx.link()
.callback(|_| Msg::GoToMenu(DocMenu::Checkbox))}
/>
<MenuItem
text={html!("Collapse")}
onclick={ctx.link()
.callback(|_| Msg::GoToMenu(DocMenu::Collapse))}
/>
<MenuItem
text={html!("ControlGroup")}
onclick={ctx.link()
.callback(|_| Msg::GoToMenu(DocMenu::ControlGroup))}
/>
<MenuItem
text={html!("Divider")}
onclick={ctx.link()
.callback(|_| Msg::GoToMenu(DocMenu::Divider))}
/>
<MenuItem
text={html!("HtmlSelect")}
onclick={ctx.link()
.callback(|_| Msg::GoToMenu(DocMenu::HtmlSelect))}
/>
<MenuItem
text={html!("Icon")}
onclick={ctx.link()
.callback(|_| Msg::GoToMenu(DocMenu::Icon))}
/>
<MenuItem
text={html!("InputGroup")}
onclick={ctx.link()
.callback(|_| Msg::GoToMenu(DocMenu::InputGroup))}
/>
<MenuItem
text={html!("Menu")}
onclick={ctx.link()
.callback(|_| Msg::GoToMenu(DocMenu::Menu))}
/>
<MenuItem
text={html!("NumericInput")}
onclick={ctx.link()
.callback(|_| Msg::GoToMenu(DocMenu::NumericInput))}
/>
<MenuItem
text={html!("PanelStack")}
onclick={ctx.link()
.callback(|_| Msg::GoToMenu(DocMenu::PanelStack))}
/>
<MenuItem
text={html!("ProgressBar")}
onclick={ctx.link()
.callback(|_| Msg::GoToMenu(DocMenu::ProgressBar))}
/>
<MenuItem
text={html!("Radio")}
onclick={ctx.link()
.callback(|_| Msg::GoToMenu(DocMenu::Radio))}
/>
<MenuItem
text={html!("Slider")}
onclick={ctx.link().callback(|_| Msg::GoToMenu(DocMenu::Slider))}
/>
<MenuItem
text={html!("Spinner")}
onclick={ctx.link()
.callback(|_| Msg::GoToMenu(DocMenu::Spinner))}
/>
<MenuItem
text={html!("Switch")}
onclick={ctx.link()
.callback(|_| Msg::GoToMenu(DocMenu::Switch))}
/>
<MenuItem
text={html!("Tabs")}
onclick={ctx.link()
.callback(|_| Msg::GoToMenu(DocMenu::Tabs))}
/>
<MenuItem
text={html!("Tag")}
onclick={ctx.link()
.callback(|_| Msg::GoToMenu(DocMenu::Tag))}
/>
<MenuItem
text={html!("Text")}
onclick={ctx.link()
.callback(|_| Msg::GoToMenu(DocMenu::Text))}
/>
<MenuItem
text={html!("TextArea")}
onclick={ctx.link()
.callback(|_| Msg::GoToMenu(DocMenu::TextArea))}
/>
<MenuItem
text={html!("Tree")}
onclick={ctx.link()
.callback(|_| Msg::GoToMenu(DocMenu::Tree))}
/>
// NOTE: thanks to keep this list of <MenuItem> sorted
// alphabetically (except for the light switch)
</Menu>
};
let navigation = html! {
<div class={classes!("docs-nav-wrapper")}>
<div class={classes!("docs-nav")}>
<div class={classes!("docs-nav-title")}>
<a class={classes!("docs-logo")} href="/">
{crate::include_raw_html!("logo.svg")}
</a>
<div>
<div class=classes!("bp3-navbar-heading", "docs-heading")>
<div class={classes!("bp3-navbar-heading", "docs-heading")}>
{"Yewprint"}
</div>
<a
class=classes!("bp3-text-muted")
class={classes!("bp3-text-muted")}
href="https://github.com/yewprint/yewprint"
target="_blank"
>
@ -111,174 +240,36 @@ impl Component for App {
</a>
</div>
</div>
<Menu>
<MenuItem
text={html!(go_to_theme_label)}
onclick=self.link
.callback(|_| Msg::ToggleLight)
icon=go_to_theme_icon
/>
<MenuItem
text={html!("Button")}
href=Cow::Borrowed("#button")
onclick=self.link
.callback(|_| Msg::GoToMenu(DocMenu::Button))
/>
<MenuItem
text={html!("ButtonGroup")}
href=Cow::Borrowed("#button-group")
onclick=self.link
.callback(|_| Msg::GoToMenu(DocMenu::ButtonGroup))
/>
<MenuItem
text={html!("Callout")}
href=Cow::Borrowed("#callout")
onclick=self.link
.callback(|_| Msg::GoToMenu(DocMenu::Callout))
/>
<MenuItem
text={html!("Card")}
href=Cow::Borrowed("#card")
onclick=self.link
.callback(|_| Msg::GoToMenu(DocMenu::Card))
/>
<MenuItem
text={html!("Checkbox")}
href=Cow::Borrowed("#checkbox")
onclick=self.link
.callback(|_| Msg::GoToMenu(DocMenu::Checkbox))
/>
<MenuItem
text={html!("Collapse")}
href=Cow::Borrowed("#collapse")
onclick=self.link
.callback(|_| Msg::GoToMenu(DocMenu::Collapse))
/>
<MenuItem
text={html!("ControlGroup")}
href=Cow::Borrowed("#control-group")
onclick=self.link
.callback(|_| Msg::GoToMenu(DocMenu::ControlGroup))
/>
<MenuItem
text={html!("Divider")}
href=Cow::Borrowed("#divider")
onclick=self.link
.callback(|_| Msg::GoToMenu(DocMenu::Divider))
/>
<MenuItem
text={html!("HtmlSelect")}
href=Cow::Borrowed("#html-select")
onclick=self.link
.callback(|_| Msg::GoToMenu(DocMenu::HtmlSelect))
/>
<MenuItem
text={html!("Icon")}
href=Cow::Borrowed("#icon")
onclick=self.link
.callback(|_| Msg::GoToMenu(DocMenu::Icon))
/>
<MenuItem
text={html!("InputGroup")}
href=Cow::Borrowed("#input-group")
onclick=self.link
.callback(|_| Msg::GoToMenu(DocMenu::InputGroup))
/>
<MenuItem
text={html!("Menu")}
href=Cow::Borrowed("#menu")
onclick=self.link
.callback(|_| Msg::GoToMenu(DocMenu::Menu))
/>
<MenuItem
text={html!("NumericInput")}
href=Cow::Borrowed("#numeric-input")
onclick=self.link
.callback(|_| Msg::GoToMenu(DocMenu::NumericInput))
/>
<MenuItem
text={html!("PanelStack")}
href=Cow::Borrowed("#panel-stack")
onclick=self.link
.callback(|_| Msg::GoToMenu(DocMenu::PanelStack))
/>
<MenuItem
text={html!("ProgressBar")}
href=Cow::Borrowed("#progress-bar")
onclick=self.link
.callback(|_| Msg::GoToMenu(DocMenu::ProgressBar))
/>
<MenuItem
text={html!("Radio")}
href=Cow::Borrowed("#radio")
onclick=self.link
.callback(|_| Msg::GoToMenu(DocMenu::Radio))
/>
<MenuItem
text={html!("Slider")}
href=Cow::Borrowed("#slider")
onclick=self.link.callback(|_| Msg::GoToMenu(DocMenu::Slider))
/>
<MenuItem
text={html!("Spinner")}
href=Cow::Borrowed("#spinner")
onclick=self.link
.callback(|_| Msg::GoToMenu(DocMenu::Spinner))
/>
<MenuItem
text={html!("Switch")}
href=Cow::Borrowed("#switch")
onclick=self.link
.callback(|_| Msg::GoToMenu(DocMenu::Switch))
/>
<MenuItem
text={html!("Tabs")}
href=Cow::Borrowed("#tabs")
onclick=self.link
.callback(|_| Msg::GoToMenu(DocMenu::Tabs))
/>
<MenuItem
text={html!("Tag")}
href=Cow::Borrowed("#tag")
onclick=self.link
.callback(|_| Msg::GoToMenu(DocMenu::Tag))
/>
<MenuItem
text={html!("Text")}
href=Cow::Borrowed("#text")
onclick=self.link
.callback(|_| Msg::GoToMenu(DocMenu::Text))
/>
<MenuItem
text={html!("TextArea")}
href=Cow::Borrowed("#textarea")
onclick=self.link
.callback(|_| Msg::GoToMenu(DocMenu::TextArea))
/>
<MenuItem
text={html!("Tree")}
href=Cow::Borrowed("#tree")
onclick=self.link
.callback(|_| Msg::GoToMenu(DocMenu::Tree))
/>
// NOTE: thanks to keep this list of <MenuItem> sorted
// alphabetically (except for the light switch)
</Menu>
{{ menu }}
<div class="docs-nav-sponsors">
<a href=Cow::Borrowed("https://www.netlify.com")>
<a href={"https://www.netlify.com"}>
<img
src=netlify_badge
src={netlify_badge}
alt="Deploys by Netlify"
/>
</a>
</div>
</div>
</div>
<main class=classes!("docs-content-wrapper") role="main">
<div class=classes!("docs-page")>
<Router<DocMenu, ()>
render=Router::render(|switch: DocMenu| {
match switch {
};
html! {
<div class={classes!("docs-root", self.dark_theme.then(|| "bp3-dark"))}>
<div class={classes!("docs-app")}>
{{ navigation }}
<main class={classes!("docs-content-wrapper")} role="main">
<div class={classes!("docs-page")}>
<Switch<DocMenu> render={Switch::render(switch)} />
</div>
</main>
</div>
</div>
}
}
}
fn switch(route: &DocMenu) -> Html {
match route {
DocMenu::Button | DocMenu::Home => html! (<ButtonDoc />),
DocMenu::ButtonGroup => html! (<ButtonGroupDoc />),
DocMenu::Callout => html!(<CalloutDoc />),
@ -304,66 +295,58 @@ impl Component for App {
DocMenu::TextArea => html!(<TextAreaDoc />),
DocMenu::Tree => html!(<TreeDoc />),
}
})
/>
</div>
</main>
</div>
</div>
}
}
}
#[derive(Debug, Copy, Clone, Switch)]
#[derive(PartialEq, Clone, Routable)]
pub enum DocMenu {
#[to = "/#button-group"]
#[at("/button-group")]
ButtonGroup,
#[to = "/#button"]
#[at("/button")]
Button,
#[to = "/#callout"]
#[at("/callout")]
Callout,
#[to = "/#card"]
#[at("/card")]
Card,
#[to = "/#checkbox"]
#[at("/checkbox")]
Checkbox,
#[to = "/#collapse"]
#[at("/collapse")]
Collapse,
#[to = "/#control-group"]
#[at("/control-group")]
ControlGroup,
#[to = "/#html-select"]
#[at("/html-select")]
HtmlSelect,
#[to = "/#divider"]
#[at("/divider")]
Divider,
#[to = "/#icon"]
#[at("/icon")]
Icon,
#[to = "/#input-group"]
#[at("/input-group")]
InputGroup,
#[to = "/#menu"]
#[at("/menu")]
Menu,
#[to = "/#numeric-input"]
#[at("/numeric-input")]
NumericInput,
#[to = "/#panel-stack"]
#[at("/panel-stack")]
PanelStack,
#[to = "/#progress-bar"]
#[at("/progress-bar")]
ProgressBar,
#[to = "/#radio"]
#[at("/radio")]
Radio,
#[to = "/#slider"]
#[at("/slider")]
Slider,
#[to = "/#spinner"]
#[at("/spinner")]
Spinner,
#[to = "/#switch"]
#[at("/switch")]
Switch,
#[to = "/#tabs"]
#[at("/tabs")]
Tabs,
#[to = "/#tag"]
#[at("/tag")]
Tag,
#[to = "/#textarea"]
#[at("/textarea")]
TextArea,
#[to = "/#text"]
#[at("/text")]
Text,
#[to = "/#tree"]
#[at("/tree")]
Tree,
#[to = "/"]
#[at("/")]
Home,
}

View file

@ -1,11 +1,6 @@
use std::borrow::Cow;
use yew::prelude::*;
use yewprint::{Button, ButtonGroup, IconName};
pub struct Example {
props: ExampleProps,
}
#[derive(Clone, PartialEq, Properties)]
pub struct ExampleProps {
pub minimal: bool,
@ -14,40 +9,19 @@ pub struct ExampleProps {
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 {
#[function_component(Example)]
pub fn example(props: &ExampleProps) -> Html {
html! {
<ButtonGroup
minimal=self.props.minimal
fill=self.props.fill
large=self.props.large
vertical=self.props.vertical
style=Cow::Borrowed("margin:0;")
minimal={props.minimal}
fill={props.fill}
large={props.large}
vertical={props.vertical}
style={"margin:0;"}
>
<Button icon=IconName::Database>{"Queries"}</Button>
<Button icon=IconName::Function>{"Functions"}</Button>
<Button icon=IconName::Cog>{"Options"}</Button>
<Button icon={IconName::Database}> {"Queries"}</Button>
<Button icon={IconName::Function}>{"Functions"}</Button>
<Button icon={IconName::Cog}>{"Options"}</Button>
</ButtonGroup>
}
}
}

View file

@ -14,9 +14,9 @@ impl Component for ButtonGroupDoc {
type Message = ExampleProps;
type Properties = ();
fn create(_: Self::Properties, link: ComponentLink<Self>) -> Self {
fn create(ctx: &Context<Self>) -> Self {
ButtonGroupDoc {
callback: link.callback(|x| x),
callback: ctx.link().callback(|x| x),
state: ExampleProps {
minimal: false,
fill: false,
@ -26,16 +26,12 @@ impl Component for ButtonGroupDoc {
}
}
fn update(&mut self, msg: Self::Message) -> ShouldRender {
fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {
self.state = msg;
true
}
fn change(&mut self, _props: Self::Properties) -> ShouldRender {
true
}
fn view(&self) -> Html {
fn view(&self, _ctx: &Context<Self>) -> Html {
let example_props = self.state.clone();
let source = crate::include_raw_html!(
concat!(env!("OUT_DIR"), "/", file!(), ".html"),
@ -44,19 +40,19 @@ impl Component for ButtonGroupDoc {
html! {
<div>
<H1 class=classes!("docs-title")>{"Button Group"}</H1>
<H1 class={classes!("docs-title")}>{"Button Group"}</H1>
<SourceCodeUrl />
<ExampleContainer
source=source
props=Some(html! {
source={source}
props={Some(html! {
<ButtonGroupProps
callback={self.callback.clone()}
props=example_props.clone()
example_props={example_props.clone()}
>
</ButtonGroupProps>
})
})}
>
<Example with example_props />
<Example ..example_props />
</ExampleContainer>
</div>
}
@ -65,41 +61,41 @@ impl Component for ButtonGroupDoc {
crate::build_example_prop_component! {
ButtonGroupProps for ExampleProps =>
fn view(&self) -> Html {
fn view(&self, ctx: &Context<Self>) -> Html {
html! {
<div>
<H5>{"Props"}</H5>
<Switch
onclick=self.update_props(|props, _| ExampleProps {
onclick={self.update_props(ctx.props(), |props, _| ExampleProps {
minimal: !props.minimal,
..props
})
checked=self.props.minimal
label=html!("Minimal")
})}
checked={ctx.props().example_props.minimal}
label={html!("Minimal")}
/>
<Switch
onclick=self.update_props(|props, _| ExampleProps{
onclick={self.update_props(ctx.props(), |props, _| ExampleProps{
fill: !props.fill,
..props
})
checked=self.props.fill
label=html!("Fill")
})}
checked={ctx.props().example_props.fill}
label={html!("Fill")}
/>
<Switch
onclick=self.update_props(|props, _| ExampleProps{
onclick={self.update_props(ctx.props(), |props, _| ExampleProps{
large: !props.large,
..props
})
checked=self.props.large
label=html!("Large")
})}
checked={ctx.props().example_props.large}
label={html!("Large")}
/>
<Switch
onclick=self.update_props(|props, _| ExampleProps {
onclick={self.update_props(ctx.props(), |props, _| ExampleProps {
vertical: !props.vertical,
..props
})
checked=self.props.vertical
label=html!("Vertical")
})}
checked={ctx.props().example_props.vertical}
label={html!("Vertical")}
/>
</div>
}

View file

@ -2,9 +2,7 @@ use yew::prelude::*;
use yewprint::Button;
pub struct Example {
link: ComponentLink<Self>,
counter: i64,
props: ExampleProps,
}
#[derive(Clone, PartialEq, Properties)]
@ -27,45 +25,32 @@ impl Component for Example {
type Message = Msg;
type Properties = ExampleProps;
fn create(props: Self::Properties, link: ComponentLink<Self>) -> Self {
Example {
counter: 0,
link,
props,
}
fn create(_ctx: &Context<Self>) -> Self {
Example { counter: 0 }
}
fn update(&mut self, msg: Self::Message) -> ShouldRender {
fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {
match msg {
Msg::AddOne => self.counter += 1,
}
true
}
fn change(&mut self, props: Self::Properties) -> ShouldRender {
if self.props != props {
self.props = props;
true
} else {
false
}
}
fn view(&self) -> Html {
fn view(&self, ctx: &Context<Self>) -> Html {
html! {
<div>
<p>{"Counter: "}{self.counter}</p>
<div>
<Button
onclick=self.link.callback(|_| Msg::AddOne)
minimal=self.props.minimal
fill=self.props.fill
small=self.props.small
outlined=self.props.outlined
loading=self.props.loading
large=self.props.large
active=self.props.active
disabled=self.props.disabled
onclick={ctx.link().callback(|_| Msg::AddOne)}
minimal={ctx.props().minimal}
fill={ctx.props().fill}
small={ctx.props().small}
outlined={ctx.props().outlined}
loading={ctx.props().loading}
large={ctx.props().large}
active={ctx.props().active}
disabled={ctx.props().disabled}
>
{"Add 1"}
</Button>

View file

@ -14,9 +14,9 @@ impl Component for ButtonDoc {
type Message = ExampleProps;
type Properties = ();
fn create(_: Self::Properties, link: ComponentLink<Self>) -> Self {
fn create(ctx: &Context<Self>) -> Self {
ButtonDoc {
callback: link.callback(|x| x),
callback: ctx.link().callback(|x| x),
state: ExampleProps {
minimal: false,
fill: false,
@ -30,37 +30,35 @@ impl Component for ButtonDoc {
}
}
fn update(&mut self, msg: Self::Message) -> ShouldRender {
fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {
self.state = msg;
true
}
fn change(&mut self, _props: Self::Properties) -> ShouldRender {
true
}
fn view(&self) -> Html {
fn view(&self, _ctx: &Context<Self>) -> Html {
let example_props = self.state.clone();
let source = crate::include_raw_html!(
concat!(env!("OUT_DIR"), "/", file!(), ".html"),
"bp3-code-block"
);
let props_component = html! {
<ButtonProps
callback={self.callback.clone()}
example_props={example_props.clone()}
/>
};
html! {
<div>
<H1 class=classes!("docs-title")>{"Button"}</H1>
<H1 class={classes!("docs-title")}>{"Button"}</H1>
<SourceCodeUrl />
<div>
<ExampleContainer
source=source
props=Some(html! {
<ButtonProps
callback={self.callback.clone()}
props=example_props.clone()
/>
})
source={source}
props={Some(props_component)}
>
<Example with example_props />
<Example ..example_props />
</ExampleContainer>
</div>
</div>
@ -70,73 +68,73 @@ impl Component for ButtonDoc {
crate::build_example_prop_component! {
ButtonProps for ExampleProps =>
fn view(&self) -> Html {
fn view(&self, ctx: &Context<Self>) -> Html {
html! {
<div>
<H5>{"Props"}</H5>
<Switch
onclick=self.update_props(|props, _| ExampleProps {
onclick={self.update_props(ctx.props(), |props, _| ExampleProps {
minimal: !props.minimal,
..props
})
checked=self.props.minimal
label=html!("Minimal")
})}
checked={ctx.props().example_props.minimal}
label={html!("Minimal")}
/>
<Switch
onclick=self.update_props(|props, _| ExampleProps {
onclick={self.update_props(ctx.props(), |props, _| ExampleProps {
fill: !props.fill,
..props
})
checked=self.props.fill
label=html!("Fill")
})}
checked={ctx.props().example_props.fill}
label={html!("Fill")}
/>
<Switch
onclick=self.update_props(|props, _| ExampleProps {
onclick={self.update_props(ctx.props(), |props, _| ExampleProps {
small: !props.small,
..props
})
checked=self.props.small
label=html!("Small")
})}
checked={ctx.props().example_props.small}
label={html!("Small")}
/>
<Switch
onclick=self.update_props(|props, _| ExampleProps {
onclick={self.update_props(ctx.props(), |props, _| ExampleProps {
outlined: !props.outlined,
..props
})
checked=self.props.outlined
label=html!("Outlined")
})}
checked={ctx.props().example_props.outlined}
label={html!("Outlined")}
/>
<Switch
onclick=self.update_props(|props, _| ExampleProps {
onclick={self.update_props(ctx.props(), |props, _| ExampleProps {
loading: !props.loading,
..props
})
checked=self.props.loading
label=html!("Loading")
})}
checked={ctx.props().example_props.loading}
label={html!("Loading")}
/>
<Switch
onclick=self.update_props(|props, _| ExampleProps {
onclick={self.update_props(ctx.props(), |props, _| ExampleProps {
large: !props.large,
..props
})
checked=self.props.large
label=html!("Large")
})}
checked={ctx.props().example_props.large}
label={html!("Large")}
/>
<Switch
onclick=self.update_props(|props, _| ExampleProps {
onclick={self.update_props(ctx.props(), |props, _| ExampleProps {
active: !props.active,
..props
})
checked=self.props.active
label=html!("Active")
})}
checked={ctx.props().example_props.active}
label={html!("Active")}
/>
<Switch
onclick=self.update_props(|props, _| ExampleProps {
onclick={self.update_props(ctx.props(), |props, _| ExampleProps {
disabled: !props.disabled,
..props
})
checked=self.props.disabled
label=html!("Disabled")
})}
checked={ctx.props().example_props.disabled}
label={html!("Disabled")}
/>
</div>
}

View file

@ -1,11 +1,6 @@
use std::borrow::Cow;
use yew::prelude::*;
use yewprint::{Callout, Intent};
pub struct Example {
props: ExampleProps,
}
#[derive(Clone, PartialEq, Properties)]
pub struct ExampleProps {
pub intent: Option<Intent>,
@ -13,36 +8,15 @@ pub struct ExampleProps {
pub show_title: 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 {
#[function_component(Example)]
pub fn example(props: &ExampleProps) -> Html {
html! {
<Callout
title=self.props.show_title.then(|| Cow::Borrowed("Visually important content"))
without_icon=!self.props.show_icon
intent=self.props.intent
title={props.show_title.then(|| "Visually important content")}
without_icon={!props.show_icon}
intent={props.intent}
>
<p>{"The Callout element's background reflects its intent, if any."}</p>
</Callout>
}
}
}

View file

@ -14,9 +14,9 @@ impl Component for CalloutDoc {
type Message = ExampleProps;
type Properties = ();
fn create(_: Self::Properties, link: ComponentLink<Self>) -> Self {
fn create(ctx: &Context<Self>) -> Self {
CalloutDoc {
callback: link.callback(|x| x),
callback: ctx.link().callback(|x| x),
state: ExampleProps {
show_icon: false,
intent: None,
@ -25,16 +25,12 @@ impl Component for CalloutDoc {
}
}
fn update(&mut self, msg: Self::Message) -> ShouldRender {
fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {
self.state = msg;
true
}
fn change(&mut self, _props: Self::Properties) -> ShouldRender {
true
}
fn view(&self) -> Html {
fn view(&self, _ctx: &Context<Self>) -> Html {
let example_props = self.state.clone();
let source = crate::include_raw_html!(
concat!(env!("OUT_DIR"), "/", file!(), ".html"),
@ -43,18 +39,18 @@ impl Component for CalloutDoc {
html! {
<div>
<H1 class=classes!("docs-title")>{"Callout"}</H1>
<H1 class={classes!("docs-title")}>{"Callout"}</H1>
<SourceCodeUrl />
<ExampleContainer
source=source
props=Some(html! {
source={source}
props={Some(html! {
<CalloutProps
callback={self.callback.clone()}
props=example_props.clone()
example_props={example_props.clone()}
/>
})
})}
>
<Example with example_props />
<Example ..example_props />
</ExampleContainer>
</div>
}
@ -63,26 +59,26 @@ impl Component for CalloutDoc {
crate::build_example_prop_component! {
CalloutProps for ExampleProps =>
fn view(&self) -> Html {
fn view(&self, ctx: &Context<Self>) -> Html {
html! {
<div>
<H5>{"Props"}</H5>
<div>
<Switch
onclick=self.update_props(|props, _| ExampleProps {
onclick={self.update_props(ctx.props(), |props, _| ExampleProps {
show_icon: !props.show_icon,
..props
})
checked=self.props.show_icon
label=html!("Show/hide icon")
})}
checked={ctx.props().example_props.show_icon}
label={html!("Show/hide icon")}
/>
<Switch
onclick=self.update_props(|props, _| ExampleProps {
onclick={self.update_props(ctx.props(), |props, _| ExampleProps {
show_title: !props.show_title,
..props
})
checked=self.props.show_title
label=html!("Show/hide title")
})}
checked={ctx.props().example_props.show_title}
label={html!("Show/hide title")}
/>
<p>{"Select intent:"}</p>
<HtmlSelect<Option<Intent>>
@ -93,10 +89,10 @@ crate::build_example_prop_component! {
(Some(Intent::Warning), "Warning".to_string()),
(Some(Intent::Danger), "Danger".to_string()),
]}
onchange=self.update_props(|props, intent| ExampleProps {
onchange={self.update_props(ctx.props(), |props, intent| ExampleProps {
intent,
..props
})
})}
/>
</div>
</div>

View file

@ -1,40 +1,16 @@
use yew::prelude::*;
use yewprint::{Card, Elevation};
pub struct Example {
props: ExampleProps,
}
#[derive(Clone, PartialEq, Properties)]
pub struct ExampleProps {
pub elevation: Elevation,
pub interactive: 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 {
#[function_component(Example)]
pub fn example(props: &ExampleProps) -> Html {
html! {
<Card elevation=self.props.elevation interactive=self.props.interactive>
<Card elevation={props.elevation} interactive={props.interactive}>
<p>
{
"This is a card component. The elevation of the card can be adjusted. \
@ -44,4 +20,3 @@ impl Component for Example {
</Card>
}
}
}

View file

@ -14,9 +14,9 @@ impl Component for CardDoc {
type Message = ExampleProps;
type Properties = ();
fn create(_: Self::Properties, link: ComponentLink<Self>) -> Self {
fn create(ctx: &Context<Self>) -> Self {
CardDoc {
callback: link.callback(|x| x),
callback: ctx.link().callback(|x| x),
state: ExampleProps {
elevation: Elevation::Level0,
interactive: false,
@ -24,16 +24,12 @@ impl Component for CardDoc {
}
}
fn update(&mut self, msg: Self::Message) -> ShouldRender {
fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {
self.state = msg;
true
}
fn change(&mut self, _props: Self::Properties) -> ShouldRender {
true
}
fn view(&self) -> Html {
fn view(&self, _ctx: &Context<Self>) -> Html {
let example_props = self.state.clone();
let source = crate::include_raw_html!(
concat!(env!("OUT_DIR"), "/", file!(), ".html"),
@ -42,18 +38,18 @@ impl Component for CardDoc {
html! {
<div>
<H1 class=classes!("docs-title")>{"Card"}</H1>
<H1 class={classes!("docs-title")}>{"Card"}</H1>
<SourceCodeUrl />
<ExampleContainer
source=source
props=Some(html! {
source={source}
props={Some(html! {
<CardProps
callback={self.callback.clone()}
props=example_props.clone()
example_props={example_props.clone()}
/>
})
})}
>
<Example with example_props />
<Example ..example_props />
</ExampleContainer>
</div>
}
@ -62,18 +58,20 @@ impl Component for CardDoc {
crate::build_example_prop_component! {
CardProps for ExampleProps =>
fn view(&self) -> Html {
fn view(&self, ctx: &Context<Self>) -> Html {
let props = ctx.props();
html! {
<div>
<H5>{"Props"}</H5>
<div>
<Switch
onclick=self.update_props(|props, _| ExampleProps {
onclick={self.update_props(props, |props, _| ExampleProps {
interactive: !props.interactive,
..props
})
checked=self.props.interactive
label=html!("Toggle interaction")
})}
checked={ctx.props().example_props.interactive}
label={html!("Toggle interaction")}
/>
<p>{"Elevation:"}</p>
<HtmlSelect<Elevation>
@ -84,11 +82,11 @@ crate::build_example_prop_component! {
(Elevation::Level3, "Level 3".to_string()),
(Elevation::Level4, "Level 4".to_string()),
]}
value=self.props.elevation
onchange=self.update_props(|props, elevation| ExampleProps {
value={ctx.props().example_props.elevation}
onchange={self.update_props(props, |props, elevation| ExampleProps {
elevation,
..props
})
})}
/>
</div>
</div>

View file

@ -1,10 +1,6 @@
use yew::prelude::*;
use yewprint::{Checkbox, Label};
pub struct Example {
props: ExampleProps,
}
#[derive(Clone, PartialEq, Properties)]
pub struct ExampleProps {
pub disabled: bool,
@ -12,50 +8,29 @@ pub struct ExampleProps {
pub large: 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 {
#[function_component(Example)]
pub fn example(props: &ExampleProps) -> Html {
html! {
<div>
<Label>{"Assign responsability"}</Label>
<Checkbox
disabled=self.props.disabled
inline=self.props.inline
large=self.props.large
label=html!("Gilad Gray")
disabled={props.disabled}
inline={props.inline}
large={props.large}
label={html!("Gilad Gray")}
/>
<Checkbox
disabled=self.props.disabled
inline=self.props.inline
large=self.props.large
label=html!("Jason Killian")
disabled={props.disabled}
inline={props.inline}
large={props.large}
label={html!("Jason Killian")}
/>
<Checkbox
disabled=self.props.disabled
inline=self.props.inline
large=self.props.large
label=html!("Antoine Llorca")
disabled={props.disabled}
inline={props.inline}
large={props.large}
label={html!("Antoine Llorca")}
/>
</div>
}
}
}

View file

@ -14,9 +14,9 @@ impl Component for CheckboxDoc {
type Message = ExampleProps;
type Properties = ();
fn create(_: Self::Properties, link: ComponentLink<Self>) -> Self {
fn create(ctx: &Context<Self>) -> Self {
CheckboxDoc {
callback: link.callback(|x| x),
callback: ctx.link().callback(|x| x),
state: ExampleProps {
disabled: false,
inline: false,
@ -25,16 +25,12 @@ impl Component for CheckboxDoc {
}
}
fn update(&mut self, msg: Self::Message) -> ShouldRender {
fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {
self.state = msg;
true
}
fn change(&mut self, _props: Self::Properties) -> ShouldRender {
true
}
fn view(&self) -> Html {
fn view(&self, _ctx: &Context<Self>) -> Html {
let example_props = self.state.clone();
let source = crate::include_raw_html!(
concat!(env!("OUT_DIR"), "/", file!(), ".html"),
@ -43,18 +39,18 @@ impl Component for CheckboxDoc {
html! {
<div>
<H1 class=classes!("docs-title")>{"Checkbox"}</H1>
<H1 class={classes!("docs-title")}>{"Checkbox"}</H1>
<SourceCodeUrl />
<ExampleContainer
source=source
props=Some(html! {
source={source}
props={Some(html! {
<CheckboxProps
callback={self.callback.clone()}
props=example_props.clone()
example_props={example_props.clone()}
/>
})
})}
>
<Example with example_props />
<Example ..example_props />
</ExampleContainer>
</div>
}
@ -63,33 +59,33 @@ impl Component for CheckboxDoc {
crate::build_example_prop_component! {
CheckboxProps for ExampleProps =>
fn view(&self) -> Html {
fn view(&self, ctx: &Context<Self>) -> Html {
html! {
<div>
<H5>{"Props"}</H5>
<Switch
onclick=self.update_props(|props, _| ExampleProps {
onclick={self.update_props(ctx.props(), |props, _| ExampleProps {
disabled: !props.disabled,
..props
})
checked=self.props.disabled
label=html!("Disabled")
})}
checked={ctx.props().example_props.disabled}
label={html!("Disabled")}
/>
<Switch
onclick=self.update_props(|props, _| ExampleProps {
onclick={self.update_props(ctx.props(), |props, _| ExampleProps {
inline: !props.inline,
..props
})
checked=self.props.inline
label=html!("Inline")
})}
checked={ctx.props().example_props.inline}
label={html!("Inline")}
/>
<Switch
onclick=self.update_props(|props, _| ExampleProps {
onclick={self.update_props(ctx.props(), |props, _| ExampleProps {
large: !props.large,
..props
})
checked=self.props.large
label=html!("Large")
})}
checked={ctx.props().example_props.large}
label={html!("Large")}
/>
</div>
}

View file

@ -2,7 +2,6 @@ use yew::prelude::*;
use yewprint::{Button, Collapse};
pub struct Example {
link: ComponentLink<Self>,
collapsed: bool,
}
@ -14,34 +13,27 @@ impl Component for Example {
type Message = Msg;
type Properties = ();
fn create(_: Self::Properties, link: ComponentLink<Self>) -> Self {
Example {
collapsed: true,
link,
}
fn create(_ctx: &Context<Self>) -> Self {
Example { collapsed: true }
}
fn update(&mut self, msg: Self::Message) -> ShouldRender {
fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {
match msg {
Msg::ToggleCollapse => self.collapsed ^= true,
}
true
}
fn change(&mut self, _props: Self::Properties) -> ShouldRender {
true
}
fn view(&self) -> Html {
fn view(&self, ctx: &Context<Self>) -> Html {
let logs = include_str!("example.log");
html! {
<div>
<Button onclick=self.link.callback(|_| Msg::ToggleCollapse)>
<Button onclick={ctx.link().callback(|_| Msg::ToggleCollapse)}>
{"Toggle collapse"}
</Button>
<Collapse
is_open=!self.collapsed
is_open={!self.collapsed}
keep_children_mounted=true
>
<pre class="bp3-code-block">

View file

@ -11,19 +11,15 @@ impl Component for CollapseDoc {
type Message = ();
type Properties = ();
fn create(_: Self::Properties, _link: ComponentLink<Self>) -> Self {
fn create(_ctx: &Context<Self>) -> Self {
CollapseDoc
}
fn update(&mut self, _msg: Self::Message) -> ShouldRender {
fn update(&mut self, _ctx: &Context<Self>, _msg: Self::Message) -> bool {
true
}
fn change(&mut self, _props: Self::Properties) -> ShouldRender {
true
}
fn view(&self) -> Html {
fn view(&self, _ctx: &Context<Self>) -> Html {
let source = crate::include_raw_html!(
concat!(env!("OUT_DIR"), "/", file!(), ".html"),
"bp3-code-block"
@ -31,9 +27,9 @@ impl Component for CollapseDoc {
html! {
<div>
<H1 class=classes!("docs-title")>{"Collapse"}</H1>
<H1 class={classes!("docs-title")}>{"Collapse"}</H1>
<SourceCodeUrl />
<ExampleContainer source=source>
<ExampleContainer source={source}>
<Example />
</ExampleContainer>
</div>

View file

@ -1,42 +1,18 @@
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 {
#[function_component(Example)]
pub fn example(props: &ExampleProps) -> Html {
html! {
<ControlGroup
fill=self.props.fill
vertical=self.props.vertical
fill={props.fill}
vertical={props.vertical}
>
<HtmlSelect<Option<Sorting>>
options={vec![
@ -48,11 +24,10 @@ impl Component for Example {
]}
/>
<InputGroup placeholder="Find filters..." />
<Button icon=IconName::ArrowRight />
<Button icon={IconName::ArrowRight} />
</ControlGroup>
}
}
}
#[derive(Debug, Clone, Copy, Hash, PartialEq)]
pub enum Sorting {

View file

@ -14,9 +14,9 @@ impl Component for ControlGroupDoc {
type Message = ExampleProps;
type Properties = ();
fn create(_: Self::Properties, link: ComponentLink<Self>) -> Self {
fn create(ctx: &Context<Self>) -> Self {
ControlGroupDoc {
callback: link.callback(|x| x),
callback: ctx.link().callback(|x| x),
state: ExampleProps {
fill: false,
vertical: false,
@ -24,16 +24,12 @@ impl Component for ControlGroupDoc {
}
}
fn update(&mut self, msg: Self::Message) -> ShouldRender {
fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {
self.state = msg;
true
}
fn change(&mut self, _props: Self::Properties) -> ShouldRender {
true
}
fn view(&self) -> Html {
fn view(&self, _ctx: &Context<Self>) -> Html {
let example_props = self.state.clone();
let source = crate::include_raw_html!(
concat!(env!("OUT_DIR"), "/", file!(), ".html"),
@ -42,19 +38,19 @@ impl Component for ControlGroupDoc {
html! {
<div>
<H1 class=classes!("docs-title")>{"ControlGroup"}</H1>
<H1 class={classes!("docs-title")}>{"ControlGroup"}</H1>
<SourceCodeUrl />
<ExampleContainer
source=source
props=Some(html! {
source={source}
props={Some(html! {
<ControlGroupProps
callback={self.callback.clone()}
props=example_props.clone()
example_props={example_props.clone()}
>
</ControlGroupProps>
})
})}
>
<Example with example_props />
<Example ..example_props />
</ExampleContainer>
</div>
}
@ -63,25 +59,25 @@ impl Component for ControlGroupDoc {
crate::build_example_prop_component! {
ControlGroupProps for ExampleProps =>
fn view(&self) -> Html {
fn view(&self, ctx: &Context<Self>) -> Html {
html! {
<div>
<H5>{"Props"}</H5>
<Switch
onclick=self.update_props(|props, _| ExampleProps {
onclick={self.update_props(ctx.props(), |props, _| ExampleProps {
fill: !props.fill,
..props
})
checked=self.props.fill
label=html!("Fill")
})}
checked={ctx.props().example_props.fill}
label={html!("Fill")}
/>
<Switch
onclick=self.update_props(|props, _| ExampleProps {
onclick={self.update_props(ctx.props(), |props, _| ExampleProps {
vertical: !props.vertical,
..props
})
checked=self.props.vertical
label=html!("Vertical")
})}
checked={ctx.props().example_props.vertical}
label={html!("Vertical")}
/>
</div>
}

View file

@ -1,48 +1,22 @@
use yew::prelude::*;
use yewprint::{Button, ButtonGroup, Divider};
pub struct Example {
props: ExampleProps,
}
#[derive(Clone, PartialEq, Properties)]
pub struct ExampleProps {
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 {
#[function_component(Example)]
pub fn example(props: &ExampleProps) -> Html {
html! {
<ButtonGroup vertical=self.props.vertical>
<ButtonGroup vertical={props.vertical}>
<Button>{"File"}</Button>
<Button>{"Edit"}</Button>
<Divider vertical=self.props.vertical />
<Divider vertical={props.vertical} />
<Button>{"Create"}</Button>
<Button>{"Delete"}</Button>
<Divider vertical=self.props.vertical />
<Divider vertical={props.vertical} />
// <Button icon=IconName::Add />
// <Button icon=IconName::Remove />
</ButtonGroup>
}
}
}

View file

@ -14,23 +14,19 @@ impl Component for DividerDoc {
type Message = ExampleProps;
type Properties = ();
fn create(_: Self::Properties, link: ComponentLink<Self>) -> Self {
fn create(ctx: &Context<Self>) -> Self {
DividerDoc {
callback: link.callback(|x| x),
callback: ctx.link().callback(|x| x),
state: ExampleProps { vertical: false },
}
}
fn update(&mut self, msg: Self::Message) -> ShouldRender {
fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {
self.state = msg;
true
}
fn change(&mut self, _props: Self::Properties) -> ShouldRender {
true
}
fn view(&self) -> Html {
fn view(&self, _ctx: &Context<Self>) -> Html {
let example_props = self.state.clone();
let source = crate::include_raw_html!(
concat!(env!("OUT_DIR"), "/", file!(), ".html"),
@ -39,18 +35,18 @@ impl Component for DividerDoc {
html! {
<div>
<H1 class=classes!("docs-title")>{"Divider"}</H1>
<H1 class={classes!("docs-title")}>{"Divider"}</H1>
<SourceCodeUrl />
<ExampleContainer
source=source
props=Some(html! {
source={source}
props={Some(html! {
<DividerProps
callback={self.callback.clone()}
props=example_props.clone()
example_props={example_props.clone()}
/>
})
})}
>
<Example with example_props />
<Example ..example_props />
</ExampleContainer>
</div>
}
@ -59,16 +55,16 @@ impl Component for DividerDoc {
crate::build_example_prop_component! {
DividerProps for ExampleProps =>
fn view(&self) -> Html {
fn view(&self, ctx: &Context<Self>) -> Html {
html! {
<div>
<H5>{"Props"}</H5>
<Switch
onclick=self.update_props(|props, _| ExampleProps {
onclick={self.update_props(ctx.props(), |props, _| ExampleProps {
vertical: !props.vertical
})
checked=self.props.vertical
label=html!("Vertical")
})}
checked={ctx.props().example_props.vertical}
label={html!("Vertical")}
/>
</div>
}

View file

@ -3,8 +3,6 @@ use yewprint::{Button, Collapse, IconName, Intent};
pub struct ExampleContainer {
collapsed: bool,
props: ExampleContainerProps,
link: ComponentLink<Self>,
}
pub enum Msg {
@ -23,41 +21,28 @@ impl Component for ExampleContainer {
type Message = Msg;
type Properties = ExampleContainerProps;
fn create(props: Self::Properties, link: ComponentLink<Self>) -> Self {
ExampleContainer {
collapsed: true,
props,
link,
}
fn create(_ctx: &Context<Self>) -> Self {
ExampleContainer { collapsed: true }
}
fn update(&mut self, msg: Self::Message) -> ShouldRender {
fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {
match msg {
Msg::ToggleSource => self.collapsed ^= true,
}
true
}
fn change(&mut self, props: Self::Properties) -> ShouldRender {
if self.props != props {
self.props = props;
true
} else {
false
}
}
fn view(&self) -> Html {
fn view(&self, ctx: &Context<Self>) -> Html {
html! {
<div class=classes!("docs-example-wrapper")>
<div class=classes!("docs-example-frame", "docs-example-frame-row")>
<div class=classes!("docs-example")>
{self.props.children.clone()}
<div class={classes!("docs-example-wrapper")}>
<div class={classes!("docs-example-frame", "docs-example-frame-row")}>
<div class={classes!("docs-example")}>
{ctx.props().children.clone()}
</div>
{
if let Some(props) = self.props.props.clone() {
if let Some(props) = ctx.props().props.clone() {
html! {
<div class=classes!("docs-example-options")>
<div class={classes!("docs-example-options")}>
{props}
</div>
}
@ -66,21 +51,21 @@ impl Component for ExampleContainer {
}
}
</div>
<div class=classes!("docs-source")>
<div class={classes!("docs-source")}>
<Button
icon=IconName::Code
fill={true}
intent={Intent::Primary}
minimal={true}
onclick=self.link.callback(|_| Msg::ToggleSource)
icon={IconName::Code}
fill={{true}}
intent={{Intent::Primary}}
minimal={{true}}
onclick={ctx.link().callback(|_| Msg::ToggleSource)}
>
{"View source"}
</Button>
<Collapse
is_open=!self.collapsed
is_open={!self.collapsed}
keep_children_mounted=true
>
{self.props.source.clone()}
{ctx.props().source.clone()}
</Collapse>
</div>
</div>
@ -88,47 +73,40 @@ impl Component for ExampleContainer {
}
}
/// The macro generates the component that will be used to render the editable
/// properties of an associated example.
#[macro_export]
macro_rules! build_example_prop_component {
($name:ident for $prop_component:ty => $($view:tt)*) => {
#[derive(Clone, PartialEq, Properties)]
pub struct $name {
callback: Callback<$prop_component>,
props: $prop_component,
example_props: $prop_component
}
impl Component for $name {
type Message = ();
type Properties = Self;
fn create(props: Self::Properties, _link: ComponentLink<Self>) -> Self {
props
}
fn update(&mut self, _msg: Self::Message) -> ShouldRender {
true
}
fn change(&mut self, props: Self::Properties) -> ShouldRender {
if self.props != props.props || self.callback != props.callback {
self.props = props.props;
self.callback = props.callback;
true
} else {
false
}
fn create(ctx: &Context<Self>) -> Self {
ctx.props().clone()
}
$($view)*
}
impl $name {
/// Propagate the prop changes to the parent so the example is
/// re-rendered.
fn update_props<T>(
&self,
props: &Self,
updater: impl Fn($prop_component, T) -> $prop_component + 'static,
) -> Callback<T> {
let props = self.props.clone();
self.callback.clone().reform(move |event| updater(props.clone(), event))
let example_props = props.example_props.clone();
self.callback
.clone()
.reform(move |event| updater(example_props.clone(), event))
}
}
};

View file

@ -2,8 +2,6 @@ use yew::prelude::*;
use yewprint::{HtmlSelect, Text};
pub struct Example {
props: ExampleProps,
link: ComponentLink<Self>,
log_level: LogLevel,
}
@ -19,29 +17,18 @@ impl Component for Example {
type Message = LogLevel;
type Properties = ExampleProps;
fn create(props: Self::Properties, link: ComponentLink<Self>) -> Self {
fn create(_ctx: &Context<Self>) -> Self {
Example {
props,
link,
log_level: LogLevel::Info,
}
}
fn update(&mut self, msg: Self::Message) -> ShouldRender {
fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {
self.log_level = msg;
true
}
fn change(&mut self, props: Self::Properties) -> ShouldRender {
if self.props != props {
self.props = props;
true
} else {
false
}
}
fn view(&self) -> Html {
fn view(&self, ctx: &Context<Self>) -> Html {
html! {
<div style="width: 400px; text-align: center;">
<HtmlSelect<LogLevel>
@ -53,13 +40,13 @@ impl Component for Example {
(LogLevel::Error, "ERROR".to_string()),
(LogLevel::Off, "OFF".to_string()),
]}
minimal=self.props.minimal
fill=self.props.fill
disabled=self.props.disabled
large=self.props.large
value=Some(self.log_level)
onchange=self.link.callback(|x| x)
title=format!("Selected: {:?}", self.log_level)
minimal={ctx.props().minimal}
fill={ctx.props().fill}
disabled={ctx.props().disabled}
large={ctx.props().large}
value={Some(self.log_level)}
onchange={ctx.link().callback(|x| x)}
title={format!("Selected: {:?}", self.log_level)}
/>
<Text>{format!("Selected: {:?}", self.log_level)}</Text>
</div>

View file

@ -14,9 +14,9 @@ impl Component for HtmlSelectDoc {
type Message = ExampleProps;
type Properties = ();
fn create(_: Self::Properties, link: ComponentLink<Self>) -> Self {
fn create(ctx: &Context<Self>) -> Self {
HtmlSelectDoc {
callback: link.callback(|x| x),
callback: ctx.link().callback(|x| x),
state: ExampleProps {
minimal: false,
fill: false,
@ -26,16 +26,12 @@ impl Component for HtmlSelectDoc {
}
}
fn update(&mut self, msg: Self::Message) -> ShouldRender {
fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {
self.state = msg;
true
}
fn change(&mut self, _props: Self::Properties) -> ShouldRender {
true
}
fn view(&self) -> Html {
fn view(&self, _ctx: &Context<Self>) -> Html {
let example_props = self.state.clone();
let source = crate::include_raw_html!(
concat!(env!("OUT_DIR"), "/", file!(), ".html"),
@ -44,19 +40,19 @@ impl Component for HtmlSelectDoc {
html! {
<div>
<H1 class=classes!("docs-title")>{"HtmlSelect"}</H1>
<H1 class={classes!("docs-title")}>{"HtmlSelect"}</H1>
<SourceCodeUrl />
<div>
<ExampleContainer
source=source
props=Some(html! {
source={source}
props={Some(html! {
<HtmlSelectProps
callback={self.callback.clone()}
props=example_props.clone()
example_props={example_props.clone()}
/>
})
})}
>
<Example with example_props />
<Example ..example_props />
</ExampleContainer>
</div>
</div>
@ -66,41 +62,41 @@ impl Component for HtmlSelectDoc {
crate::build_example_prop_component! {
HtmlSelectProps for ExampleProps =>
fn view(&self) -> Html {
fn view(&self, ctx: &Context<Self>) -> Html {
html! {
<div>
<H5>{"Props"}</H5>
<Switch
onclick=self.update_props(|props, _| ExampleProps {
onclick={self.update_props(ctx.props(), |props, _| ExampleProps {
minimal: !props.minimal,
..props
})
checked=self.props.minimal
label=html!("Minimal")
})}
checked={ctx.props().example_props.minimal}
label={html!("Minimal")}
/>
<Switch
onclick=self.update_props(|props, _| ExampleProps{
onclick={self.update_props(ctx.props(), |props, _| ExampleProps{
fill: !props.fill,
..props
})
checked=self.props.fill
label=html!("Fill")
})}
checked={ctx.props().example_props.fill}
label={html!("Fill")}
/>
<Switch
onclick=self.update_props(|props, _| ExampleProps {
onclick={self.update_props(ctx.props(), |props, _| ExampleProps {
disabled: !props.disabled,
..props
})
checked=self.props.disabled
label=html!("Disabled")
})}
checked={ctx.props().example_props.disabled}
label={html!("Disabled")}
/>
<Switch
onclick=self.update_props(|props, _| ExampleProps{
onclick={self.update_props(ctx.props(), |props, _| ExampleProps{
large: !props.large,
..props
})
checked=self.props.large
label=html!("Large")
})}
checked={ctx.props().example_props.large}
label={html!("Large")}
/>
</div>
}

View file

@ -1,26 +1,11 @@
use yew::prelude::*;
use yewprint::{Icon, IconName};
pub struct Example {}
impl Component for Example {
type Message = ();
type Properties = ();
fn create(_: Self::Properties, _link: ComponentLink<Self>) -> Self {
Example {}
}
fn update(&mut self, _msg: Self::Message) -> ShouldRender {
true
}
fn change(&mut self, _props: Self::Properties) -> ShouldRender {
true
}
fn view(&self) -> Html {
#[function_component(Example)]
pub fn example() -> Html {
html! {
<div>
<Icon icon=IconName::Print />
<Icon icon={IconName::Print} />
</div>
}
}
}

View file

@ -11,19 +11,15 @@ impl Component for IconDoc {
type Message = ();
type Properties = ();
fn create(_: Self::Properties, _link: ComponentLink<Self>) -> Self {
fn create(_ctx: &Context<Self>) -> Self {
IconDoc
}
fn update(&mut self, _msg: Self::Message) -> ShouldRender {
fn update(&mut self, _ctx: &Context<Self>, _msg: Self::Message) -> bool {
true
}
fn change(&mut self, _props: Self::Properties) -> ShouldRender {
true
}
fn view(&self) -> Html {
fn view(&self, _ctx: &Context<Self>) -> Html {
let source = crate::include_raw_html!(
concat!(env!("OUT_DIR"), "/", file!(), ".html"),
"bp3-code-block"
@ -31,9 +27,9 @@ impl Component for IconDoc {
html! {
<div>
<H1 class=classes!("docs-title")>{"Icon"}</H1>
<H1 class={classes!("docs-title")}>{"Icon"}</H1>
<SourceCodeUrl />
<ExampleContainer source=source>
<ExampleContainer source={source}>
<Example />
</ExampleContainer>
</div>

View file

@ -1,9 +1,9 @@
use gloo::dialogs::alert;
use web_sys::HtmlInputElement;
use yew::prelude::*;
use yewprint::{Button, IconName, InputGroup, Tag};
pub struct Example {
link: ComponentLink<Self>,
props: ExampleProps,
histogram_value: String,
password_value: String,
password_strength: Html,
@ -29,22 +29,12 @@ pub enum Msg {
Noop,
}
macro_rules! alert {
($($arg:tt)*) => {
yew::services::DialogService::alert(&format!(
$($arg)*
))
};
}
impl Component for Example {
type Message = Msg;
type Properties = ExampleProps;
fn create(props: Self::Properties, link: ComponentLink<Self>) -> Self {
fn create(_ctx: &Context<Self>) -> Self {
Example {
props,
link,
histogram_value: Default::default(),
password_value: Default::default(),
password_strength: Default::default(),
@ -52,10 +42,10 @@ impl Component for Example {
}
}
fn update(&mut self, msg: Self::Message) -> bool {
fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {
match msg {
Msg::AddHistogramEntry => {
alert!("You sent: {}", self.histogram_value);
alert(&format!("You sent: {}", self.histogram_value));
self.histogram_value = Default::default();
true
}
@ -64,7 +54,7 @@ impl Component for Example {
true
}
Msg::AddPasswordEntry => {
alert!("You sent: {}", self.password_value);
alert(&format!("You sent: {}", self.password_value));
self.password_value = Default::default();
true
}
@ -81,7 +71,7 @@ impl Component for Example {
true
}
Msg::AddTagsEntry => {
alert!("You sent: {}", self.tags_value);
alert(&format!("You sent: {}", self.tags_value));
self.tags_value = Default::default();
true
}
@ -93,74 +83,76 @@ impl Component for Example {
}
}
fn change(&mut self, props: Self::Properties) -> ShouldRender {
if self.props != props {
self.props = props;
true
} else {
false
}
}
fn view(&self) -> Html {
fn view(&self, ctx: &Context<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
fill={ctx.props().fill}
large={ctx.props().large}
small={ctx.props().small}
round={ctx.props().round}
disabled={ctx.props().disabled}
left_icon={IconName::Filter}
placeholder={"Filter histogram..."}
value=self.histogram_value.clone()
oninput=self.link.callback(|e: InputData| Msg::UpdateHistogram(e.value))
onkeydown=self.link.callback(|e: KeyboardEvent| {
value={self.histogram_value.clone()}
oninput={ctx.link().callback(|e: InputEvent| {
let value = e.target_unchecked_into::<HtmlInputElement>().value();
Msg::UpdateHistogram(value)
})}
onkeydown={ctx.link().callback(|e: KeyboardEvent| {
if e.key() == "Enter" { Msg::AddHistogramEntry } else { Msg::Noop }
})
})}
/>
<InputGroup
fill=self.props.fill
large=self.props.large
small=self.props.small
round=self.props.round
disabled=self.props.disabled
left_element=self.password_strength.clone()
fill={ctx.props().fill}
large={ctx.props().large}
small={ctx.props().small}
round={ctx.props().round}
disabled={ctx.props().disabled}
left_element={self.password_strength.clone()}
placeholder={"Enter your password..."}
value=self.password_value.clone()
oninput=self.link.callback(|e: InputData| Msg::UpdatePassword(e.value))
onkeydown=self.link.callback(|e: KeyboardEvent| {
value={self.password_value.clone()}
oninput={ctx.link().callback(|e: InputEvent| {
let value = e.target_unchecked_into::<HtmlInputElement>().value();
Msg::UpdatePassword(value)
})}
onkeydown={ctx.link().callback(|e: KeyboardEvent| {
if e.key() == "Enter" { Msg::AddPasswordEntry } else { Msg::Noop }
})
right_element=html! {
})}
right_element={{ html! {
<Button
icon=IconName::Lock
minimal=true
disabled=self.props.disabled
icon={IconName::Lock}
minimal={true}
disabled={ctx.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
fill={ctx.props().fill}
large={ctx.props().large}
small={ctx.props().small}
round={ctx.props().round}
disabled={ctx.props().disabled}
left_icon={IconName::Tag}
placeholder={"Find tags"}
value=self.tags_value.clone()
oninput=self.link.callback(|e: InputData| Msg::UpdateTags(e.value))
onkeydown=self.link.callback(|e: KeyboardEvent| {
value={self.tags_value.clone()}
oninput={ctx.link().callback(|e: InputEvent| {
let value = e.target_unchecked_into::<HtmlInputElement>().value();
Msg::UpdateTags(value)
})}
onkeydown={ctx.link().callback(|e: KeyboardEvent| {
if e.key() == "Enter" { Msg::AddTagsEntry } else { Msg::Noop }
})
right_element=html! {
})}
right_element={{
html!{
<Tag
minimal=true
round=self.props.round
minimal={true}
round={ctx.props().round}
>
{{10000 / 1.max(self.tags_value.len().pow(2))}}
{ (10000 / 1.max(self.tags_value.len().pow(2))).to_string() }
</Tag>
}
}}
/>
</>
}

View file

@ -14,9 +14,9 @@ impl Component for InputGroupDoc {
type Message = ExampleProps;
type Properties = ();
fn create(_: Self::Properties, link: ComponentLink<Self>) -> Self {
fn create(ctx: &Context<Self>) -> Self {
InputGroupDoc {
callback: link.callback(|x| x),
callback: ctx.link().callback(|x| x),
state: ExampleProps {
disabled: false,
fill: false,
@ -27,16 +27,12 @@ impl Component for InputGroupDoc {
}
}
fn update(&mut self, msg: Self::Message) -> ShouldRender {
fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {
self.state = msg;
true
}
fn change(&mut self, _props: Self::Properties) -> ShouldRender {
true
}
fn view(&self) -> Html {
fn view(&self, _ctx: &Context<Self>) -> Html {
let example_props = self.state.clone();
let source = crate::include_raw_html!(
concat!(env!("OUT_DIR"), "/", file!(), ".html"),
@ -45,19 +41,19 @@ impl Component for InputGroupDoc {
html! {
<div>
<H1 class=classes!("docs-title")>{"InputGroup"}</H1>
<H1 class={classes!("docs-title")}>{"InputGroup"}</H1>
<SourceCodeUrl />
<ExampleContainer
source=source
props=Some(html! {
source={source}
props={Some(html! {
<InputGroupProps
callback={self.callback.clone()}
props=example_props.clone()
example_props={example_props.clone()}
>
</InputGroupProps>
})
})}
>
<Example with example_props />
<Example ..example_props />
</ExampleContainer>
</div>
}
@ -66,49 +62,49 @@ impl Component for InputGroupDoc {
crate::build_example_prop_component! {
InputGroupProps for ExampleProps =>
fn view(&self) -> Html {
fn view(&self, ctx: &Context<Self>) -> Html {
html! {
<div>
<H5>{"Props"}</H5>
<Switch
onclick=self.update_props(|props, _| ExampleProps {
onclick={self.update_props(ctx.props(), |props, _| ExampleProps {
disabled: !props.disabled,
..props
})
checked=self.props.disabled
label=html!("Disabled")
})}
checked={ctx.props().example_props.disabled}
label={html!("Disabled")}
/>
<Switch
onclick=self.update_props(|props, _| ExampleProps {
onclick={self.update_props(ctx.props(), |props, _| ExampleProps {
fill: !props.fill,
..props
})
checked=self.props.fill
label=html!("Fill")
})}
checked={ctx.props().example_props.fill}
label={html!("Fill")}
/>
<Switch
onclick=self.update_props(|props, _| ExampleProps {
onclick={self.update_props(ctx.props(), |props, _| ExampleProps {
large: !props.large,
..props
})
checked=self.props.large
label=html!("Large")
})}
checked={ctx.props().example_props.large}
label={html!("Large")}
/>
<Switch
onclick=self.update_props(|props, _| ExampleProps {
onclick={self.update_props(ctx.props(), |props, _| ExampleProps {
small: !props.small,
..props
})
checked=self.props.small
label=html!("Small")
})}
checked={ctx.props().example_props.small}
label={html!("Small")}
/>
<Switch
onclick=self.update_props(|props, _| ExampleProps {
onclick={self.update_props(ctx.props(), |props, _| ExampleProps {
round: !props.round,
..props
})
checked=self.props.round
label=html!("Round")
})}
checked={ctx.props().example_props.round}
label={html!("Round")}
/>
</div>
}

View file

@ -32,17 +32,10 @@ pub use app::*;
pub use example::*;
pub use logo::*;
#[macro_export]
macro_rules! log {
($s:expr $(,$args:expr)*) => {{
yew::services::ConsoleService::log(format!($s $(,$args)*).as_str());
}};
}
#[macro_export]
macro_rules! include_raw_html {
($file:expr $(, $class:expr)?) => {{
yew::virtual_dom::VNode::VRef(yew::web_sys::Node::from({
yew::virtual_dom::VNode::VRef(web_sys::Node::from({
let div = web_sys::window()
.unwrap()
.document()
@ -88,27 +81,23 @@ macro_rules! build_source_code_component {
type Message = ();
type Properties = ();
fn create(_: Self::Properties, _link: ComponentLink<Self>) -> Self {
fn create(_ctx: &Context<Self>) -> Self {
let url = SourceCodeUrl::generate_url();
Self { url }
}
fn update(&mut self, _msg: Self::Message) -> ShouldRender {
fn update(&mut self, _ctx: &Context<Self>, _msg: Self::Message) -> bool {
true
}
fn change(&mut self, _props: Self::Properties) -> ShouldRender {
true
}
fn view(&self) -> Html {
fn view(&self, _ctx: &Context<Self>) -> Html {
use yewprint::Text;
html! {
<a
class=classes!("bp3-text-muted")
href=self.url.clone()
class={classes!("bp3-text-muted")}
href={self.url.clone()}
target="_blank"
>
<Text>{"Go to source code"}</Text>
@ -142,7 +131,7 @@ static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
pub fn run_app() -> Result<(), wasm_bindgen::JsValue> {
#[cfg(feature = "console_error_panic_hook")]
console_error_panic_hook::set_once();
yew::start_app::<app::App>();
yew::start_app::<app::AppRoot>();
Ok(())
}

View file

@ -1,37 +1,12 @@
use yew::prelude::*;
pub struct Logo {
props: LogoProps,
}
#[derive(Clone, PartialEq, Properties)]
pub struct LogoProps {
#[prop_or_default]
pub class: Classes,
}
impl Component for Logo {
type Message = ();
type Properties = LogoProps;
fn create(props: Self::Properties, _: ComponentLink<Self>) -> Self {
Self { props }
}
fn update(&mut self, _msg: Self::Message) -> ShouldRender {
todo!()
}
fn change(&mut self, props: Self::Properties) -> ShouldRender {
if props != self.props {
self.props = props;
true
} else {
false
}
}
fn view(&self) -> Html {
crate::include_raw_html!("logo.svg", &self.props.class.to_string())
}
#[function_component(Logo)]
pub fn logo(props: &LogoProps) -> Html {
crate::include_raw_html!("logo.svg", &props.class.to_string())
}

View file

@ -1,11 +1,8 @@
use crate::{DocMenu, Logo};
use std::borrow::Cow;
use yew::prelude::*;
use yewprint::{Icon, IconName, Menu, MenuDivider, MenuItem};
pub struct Example {
link: ComponentLink<Self>,
}
pub struct Example {}
pub enum Msg {
GoToMenu(DocMenu),
@ -15,107 +12,106 @@ impl Component for Example {
type Message = Msg;
type Properties = ();
fn create(_: Self::Properties, link: ComponentLink<Self>) -> Self {
Self { link }
fn create(_ctx: &Context<Self>) -> Self {
Self {}
}
fn update(&mut self, _msg: Self::Message) -> ShouldRender {
fn update(&mut self, _ctx: &Context<Self>, _msg: Self::Message) -> bool {
true
}
fn change(&mut self, _props: Self::Properties) -> ShouldRender {
true
}
fn view(&self, ctx: &Context<Self>) -> Html {
let custom_icon = html! {
<Logo class={classes!("custom-icon")} />
};
let share_icon = html! {
<Icon icon={IconName::Share} />
};
fn view(&self) -> Html {
html! {
<>
<Menu>
<MenuItem
text={html!("Custom SVG icon")}
icon_html=html! {
<Logo class=classes!("custom-icon") />
}
icon_html={custom_icon}
/>
<MenuDivider />
<MenuItem
icon=IconName::NewTextBox
text={html!{"New text box"}}
href=Cow::Borrowed("#menu")
onclick=self.link
.callback(|_| Msg::GoToMenu(DocMenu::Menu))
icon={IconName::NewTextBox}
text={html!("New text box")}
href={"#menu"}
onclick={ctx.link()
.callback(|_| Msg::GoToMenu(DocMenu::Menu))}
/>
<MenuItem
icon=IconName::NewObject
text={html!{"New object"}}
href=Cow::Borrowed("#menu")
onclick=self.link
.callback(|_| Msg::GoToMenu(DocMenu::Menu))
icon={IconName::NewObject}
text={html!("New object")}
href={"#menu"}
onclick={ctx.link()
.callback(|_| Msg::GoToMenu(DocMenu::Menu))}
/>
<MenuItem
icon=IconName::NewLink
text={html!{"New link"}}
href=Cow::Borrowed("#menu")
onclick=self.link
.callback(|_| Msg::GoToMenu(DocMenu::Menu))
icon={IconName::NewLink}
text={html!("New link")}
href={"#menu"}
onclick={ctx.link()
.callback(|_| Msg::GoToMenu(DocMenu::Menu))}
/>
<MenuDivider />
<MenuItem
icon=IconName::Cog
text={html!{"Settings"}}
label=html! {
<Icon icon=IconName::Share />
}
href=Cow::Borrowed("#menu")
onclick=self.link
.callback(|_| Msg::GoToMenu(DocMenu::Menu))
icon={IconName::Cog}
text={html!("Settings")}
label={share_icon}
href={"#menu"}
onclick={ctx.link()
.callback(|_| Msg::GoToMenu(DocMenu::Menu))}
/>
</Menu>
<Menu>
<MenuDivider title={html!("Edit")} />
<MenuItem
icon=IconName::Cut
icon={IconName::Cut}
text={html!("Cut")}
label={html!("Ctrl+X")}
href=Cow::Borrowed("#menu")
onclick=self.link
.callback(|_| Msg::GoToMenu(DocMenu::Menu))
href={"#menu"}
onclick={ctx.link()
.callback(|_| Msg::GoToMenu(DocMenu::Menu))}
/>
<MenuItem
icon=IconName::Duplicate
icon={IconName::Duplicate}
text={html!("Copy")}
label={html!("Ctrl+C")}
href=Cow::Borrowed("#menu")
onclick=self.link
.callback(|_| Msg::GoToMenu(DocMenu::Menu))
href={"#menu"}
onclick={ctx.link()
.callback(|_| Msg::GoToMenu(DocMenu::Menu))}
/>
<MenuItem
icon=IconName::Clipboard
icon={IconName::Clipboard}
text={html!("Paste")}
label={html!("Ctrl+V")}
disabled=true
/>
<MenuDivider title={html!("Text")} />
<MenuItem
icon=IconName::AlignLeft
icon={IconName::AlignLeft}
text={html!("Alignment")}
href=Cow::Borrowed("#menu")
onclick=self.link
.callback(|_| Msg::GoToMenu(DocMenu::Menu))
href={"#menu"}
onclick={ctx.link()
.callback(|_| Msg::GoToMenu(DocMenu::Menu))}
/>
<MenuItem
icon=IconName::Style
icon={IconName::Style}
text={html!("Style")}
href=Cow::Borrowed("#menu")
onclick=self.link
.callback(|_| Msg::GoToMenu(DocMenu::Menu))
href={"#menu"}
onclick={ctx.link()
.callback(|_| Msg::GoToMenu(DocMenu::Menu))}
/>
<MenuItem
icon=IconName::Asterisk
icon={IconName::Asterisk}
text={html!("Miscellaneous")}
href=Cow::Borrowed("#menu")
onclick=self.link
.callback(|_| Msg::GoToMenu(DocMenu::Menu))
href={"#menu"}
onclick={ctx.link().callback(|_| Msg::GoToMenu(DocMenu::Menu))}
/>
</Menu>
</>

View file

@ -11,19 +11,15 @@ impl Component for MenuDoc {
type Message = ();
type Properties = ();
fn create(_: Self::Properties, _link: ComponentLink<Self>) -> Self {
fn create(_ctx: &Context<Self>) -> Self {
MenuDoc
}
fn update(&mut self, _msg: Self::Message) -> ShouldRender {
fn update(&mut self, _ctx: &Context<Self>, _msg: Self::Message) -> bool {
true
}
fn change(&mut self, _props: Self::Properties) -> ShouldRender {
true
}
fn view(&self) -> Html {
fn view(&self, _ctx: &Context<Self>) -> Html {
let source = crate::include_raw_html!(
concat!(env!("OUT_DIR"), "/", file!(), ".html"),
"bp3-code-block"
@ -31,9 +27,9 @@ impl Component for MenuDoc {
html! {
<div>
<H1 class=classes!("docs-title")>{"Menu"}</H1>
<H1 class={classes!("docs-title")}>{"Menu"}</H1>
<SourceCodeUrl />
<ExampleContainer source=source>
<ExampleContainer source={source}>
<Example />
</ExampleContainer>
</div>

View file

@ -1,10 +1,7 @@
use std::borrow::Cow;
use yew::prelude::*;
use yewprint::{Button, Callout, IconName, Intent, NumericInput};
pub struct Example {
props: ExampleProps,
link: ComponentLink<Self>,
value: i32,
value_two: i32,
}
@ -29,16 +26,14 @@ impl Component for Example {
type Message = Msg;
type Properties = ExampleProps;
fn create(props: Self::Properties, link: ComponentLink<Self>) -> Self {
fn create(_ctx: &Context<Self>) -> Self {
Example {
props,
link,
value: 0,
value_two: 0,
}
}
fn update(&mut self, msg: Self::Message) -> ShouldRender {
fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {
match msg {
Msg::Reset => {
self.value = 4;
@ -54,52 +49,43 @@ impl Component for Example {
true
}
fn change(&mut self, props: Self::Properties) -> ShouldRender {
if self.props != props {
self.props = props;
true
} else {
false
}
}
fn view(&self) -> Html {
fn view(&self, ctx: &Context<Self>) -> Html {
html! {
<>
<NumericInput<i32>
disabled=self.props.disabled
fill=self.props.large
value=self.value
disabled={ctx.props().disabled}
fill={ctx.props().large}
value={self.value}
bounds={-105..}
increment=10
placeholder=String::from("Greater or equal to -105...")
onchange=self.link.callback(|x| Msg::UpdateValue(x))
disable_buttons=self.props.disable_buttons
buttons_on_the_left=self.props.buttons_on_the_left
left_icon=self.props.left_icon.then(|| IconName::Dollar)
placeholder={String::from("Greater or equal to -105...")}
onchange={ctx.link().callback(|x| Msg::UpdateValue(x))}
disable_buttons={ctx.props().disable_buttons}
buttons_on_the_left={ctx.props().buttons_on_the_left}
left_icon={ctx.props().left_icon.then(|| IconName::Dollar)}
/>
<NumericInput<i32>
disabled=self.props.disabled
fill=self.props.fill
large=self.props.large
value=self.value_two
disabled={ctx.props().disabled}
fill={ctx.props().fill}
large={ctx.props().large}
value={self.value_two}
bounds={-10..=10}
increment=1
placeholder=String::from("Integer between -10 and 10")
onchange=self.link.callback(|x| Msg::UpdateValueTwo(x))
disable_buttons=self.props.disable_buttons
buttons_on_the_left=self.props.buttons_on_the_left
left_icon=self.props.left_icon.then(|| IconName::Dollar)
placeholder={String::from("Integer between -10 and 10")}
onchange={ctx.link().callback(|x| Msg::UpdateValueTwo(x))}
disable_buttons={ctx.props().disable_buttons}
buttons_on_the_left={ctx.props().buttons_on_the_left}
left_icon={ctx.props().left_icon.then(|| IconName::Dollar)}
/>
<Button
icon=IconName::Refresh
onclick=self.link.callback(|_| Msg::Reset)
icon={IconName::Refresh}
onclick={ctx.link().callback(|_| Msg::Reset)}
>
{"Reset at 4"}
</Button>
<Callout
title=Cow::Borrowed("Selected values")
intent=Intent::Primary
title={"Selected values"}
intent={Intent::Primary}
>
<ul>
<li>{self.value}</li>

View file

@ -14,9 +14,9 @@ impl Component for NumericInputDoc {
type Message = ExampleProps;
type Properties = ();
fn create(_: Self::Properties, link: ComponentLink<Self>) -> Self {
fn create(ctx: &Context<Self>) -> Self {
NumericInputDoc {
callback: link.callback(|x| x),
callback: ctx.link().callback(|x| x),
state: ExampleProps {
fill: false,
disabled: false,
@ -28,16 +28,12 @@ impl Component for NumericInputDoc {
}
}
fn update(&mut self, msg: Self::Message) -> ShouldRender {
fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {
self.state = msg;
true
}
fn change(&mut self, _props: Self::Properties) -> ShouldRender {
true
}
fn view(&self) -> Html {
fn view(&self, _ctx: &Context<Self>) -> Html {
let example_props = self.state.clone();
let source = crate::include_raw_html!(
concat!(env!("OUT_DIR"), "/", file!(), ".html"),
@ -46,19 +42,19 @@ impl Component for NumericInputDoc {
html! {
<div>
<H1 class=classes!("docs-title")>{"NumericInput"}</H1>
<H1 class={classes!("docs-title")}>{"NumericInput"}</H1>
<SourceCodeUrl />
<div>
<ExampleContainer
source=source
props=Some(html! {
source={source}
props={Some(html! {
<NumericInputProps
callback={self.callback.clone()}
props=example_props.clone()
example_props={example_props.clone()}
/>
})
})}
>
<Example with example_props />
<Example ..example_props />
</ExampleContainer>
</div>
</div>
@ -68,57 +64,57 @@ impl Component for NumericInputDoc {
crate::build_example_prop_component! {
NumericInputProps for ExampleProps =>
fn view(&self) -> Html {
fn view(&self, ctx: &Context<Self>) -> Html {
html! {
<div>
<H5>{"Props"}</H5>
<Switch
onclick=self.update_props(|props, _| ExampleProps {
onclick={self.update_props(ctx.props(), |props, _| ExampleProps {
fill: !props.fill,
..props
})
checked=self.props.fill
label=html!("Fill")
})}
checked={ctx.props().example_props.fill}
label={html!("Fill")}
/>
<Switch
onclick=self.update_props(|props, _| ExampleProps {
onclick={self.update_props(ctx.props(), |props, _| ExampleProps {
disabled: !props.disabled,
..props
})
checked=self.props.disabled
label=html!("Disabled")
})}
checked={ctx.props().example_props.disabled}
label={html!("Disabled")}
/>
<Switch
onclick=self.update_props(|props, _| ExampleProps {
onclick={self.update_props(ctx.props(), |props, _| ExampleProps {
large: !props.large,
..props
})
checked=self.props.large
label=html!("Large")
})}
checked={ctx.props().example_props.large}
label={html!("Large")}
/>
<Switch
onclick=self.update_props(|props, _| ExampleProps {
onclick={self.update_props(ctx.props(), |props, _| ExampleProps {
disable_buttons: !props.disable_buttons,
..props
})
checked=self.props.disable_buttons
label=html!("Disable buttons")
})}
checked={ctx.props().example_props.disable_buttons}
label={html!("Disable buttons")}
/>
<Switch
onclick=self.update_props(|props, _| ExampleProps {
onclick={self.update_props(ctx.props(), |props, _| ExampleProps {
buttons_on_the_left: !props.buttons_on_the_left,
..props
})
checked=self.props.buttons_on_the_left
label=html!("Buttons on the left")
})}
checked={ctx.props().example_props.buttons_on_the_left}
label={html!("Buttons on the left")}
/>
<Switch
onclick=self.update_props(|props, _| ExampleProps {
onclick={self.update_props(ctx.props(), |props, _| ExampleProps {
left_icon: !props.left_icon,
..props
})
checked=self.props.left_icon
label=html!("Left icon")
})}
checked={ctx.props().example_props.left_icon}
label={html!("Left icon")}
/>
</div>
}

View file

@ -2,8 +2,6 @@ use yew::prelude::*;
use yewprint::{Button, Intent, PanelStack, PanelStackState, Text};
pub struct Example {
link: ComponentLink<Self>,
props: ExampleProps,
state: PanelStackState,
}
@ -23,37 +21,38 @@ impl Component for Example {
type Message = ExampleMessage;
type Properties = ExampleProps;
fn create(props: Self::Properties, link: ComponentLink<Self>) -> Self {
fn create(ctx: &Context<Self>) -> Self {
let state = PanelStackState::new(html! {
<div class=classes!("docs-panel-stack-contents-example")>
<div class={classes!("docs-panel-stack-contents-example")}>
<div>{"Hello World!"}</div>
<Button
intent=Intent::Primary
onclick=link.callback(|_| ExampleMessage::OpenPanel2)
intent={Intent::Primary}
onclick={ctx.link().callback(|_| ExampleMessage::OpenPanel2)}
>
{"Open panel 2"}
</Button>
</div>
})
.with_title(html! {
<Text class=classes!("bp3-heading") ellipsize=true>
<Text class={classes!("bp3-heading")} ellipsize=true>
{"Root Panel"}
</Text>
})
.finish();
Example { link, props, state }
Example { state }
}
fn update(&mut self, msg: Self::Message) -> ShouldRender {
fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
match msg {
// Open
ExampleMessage::OpenPanel2 => self
.state
.open_panel(html! {
<div class=classes!("docs-panel-stack-contents-example")>
<div class={classes!("docs-panel-stack-contents-example")}>
<Button
intent=Intent::Success
onclick=self.link.callback(|_| ExampleMessage::OpenPanel2)
intent={Intent::Success}
onclick={ctx.link().callback(|_| ExampleMessage::OpenPanel2)}
>
{"Open another panel 2"}
</Button>
@ -61,7 +60,7 @@ impl Component for Example {
</div>
})
.with_title(html! {
<Text class=classes!("bp3-heading") ellipsize=true>
<Text class={classes!("bp3-heading")} ellipsize=true>
{"Panel 2"}
</Text>
})
@ -71,70 +70,34 @@ impl Component for Example {
}
}
fn change(&mut self, props: Self::Properties) -> ShouldRender {
if self.props != props {
self.props = props;
true
} else {
false
}
}
fn view(&self) -> Html {
fn view(&self, ctx: &Context<Self>) -> Html {
html! {
<div>
<PanelStack
state=self.state.clone()
onclose=self.link.callback(|_| ExampleMessage::ClosePanel)
class=classes!("docs-panel-stack-example")
state={self.state.clone()}
onclose={ctx.link().callback(|_| ExampleMessage::ClosePanel)}
class={classes!("docs-panel-stack-example")}
/>
</div>
}
}
}
// Second panel: a simple counter
/// Second panel: a simple counter
#[function_component(Panel2)]
pub fn panel2() -> Html {
let counter = use_state(|| 0);
let onclick = {
let counter = counter.clone();
Callback::from(move |_| counter.set(*counter + 1))
};
pub struct Panel2 {
link: ComponentLink<Self>,
counter: i64,
}
pub enum Panel2Message {
AddOne,
}
impl Component for Panel2 {
type Message = Panel2Message;
type Properties = ();
fn create(_props: Self::Properties, link: ComponentLink<Self>) -> Self {
Panel2 { counter: 0, link }
}
fn update(&mut self, msg: Self::Message) -> ShouldRender {
match msg {
Panel2Message::AddOne => {
self.counter += 1;
true
}
}
}
fn change(&mut self, _props: Self::Properties) -> ShouldRender {
false
}
fn view(&self) -> Html {
html! {
<div>
<p>{"Counter: "}{self.counter}</p>
<p>{"Counter: "}{ *counter}</p>
<div>
<Button onclick=self.link.callback(|_| Panel2Message::AddOne)>
{"Add 1"}
</Button>
<Button {onclick}>{ "Add 1" }</Button>
</div>
</div>
}
}
}

View file

@ -14,9 +14,9 @@ impl Component for PanelStackDoc {
type Message = ExampleProps;
type Properties = ();
fn create(_: Self::Properties, link: ComponentLink<Self>) -> Self {
fn create(ctx: &Context<Self>) -> Self {
PanelStackDoc {
callback: link.callback(|x| x),
callback: ctx.link().callback(|x| x),
state: ExampleProps {
animate: true,
vertical: false,
@ -24,16 +24,12 @@ impl Component for PanelStackDoc {
}
}
fn update(&mut self, msg: Self::Message) -> ShouldRender {
fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {
self.state = msg;
true
}
fn change(&mut self, _props: Self::Properties) -> ShouldRender {
true
}
fn view(&self) -> Html {
fn view(&self, _ctx: &Context<Self>) -> Html {
let example_props = self.state.clone();
let source = crate::include_raw_html!(
concat!(env!("OUT_DIR"), "/", file!(), ".html"),
@ -42,14 +38,14 @@ impl Component for PanelStackDoc {
html! {
<div>
<H1 class=classes!("docs-title")>{"PanelStack"}</H1>
<H1 class={classes!("docs-title")}>{"PanelStack"}</H1>
<SourceCodeUrl />
<div>
<ExampleContainer
source=source
props=None
source={source}
props={None}
>
<Example with example_props />
<Example ..example_props />
</ExampleContainer>
</div>
</div>

View file

@ -1,10 +1,6 @@
use yew::prelude::*;
use yewprint::{Intent, ProgressBar};
pub struct Example {
props: ExampleProps,
}
#[derive(Clone, PartialEq, Properties)]
pub struct ExampleProps {
pub intent: Option<Intent>,
@ -12,32 +8,14 @@ pub struct ExampleProps {
pub stripes: 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 {
#[function_component(Example)]
pub fn example(props: &ExampleProps) -> Html {
html! {
<ProgressBar
animate=self.props.animate
stripes=self.props.stripes
intent=self.props.intent
animate={props.animate}
stripes={props.stripes}
intent={props.intent}
value=0.3
/>
}
}
}

View file

@ -14,9 +14,9 @@ impl Component for ProgressBarDoc {
type Message = ExampleProps;
type Properties = ();
fn create(_: Self::Properties, link: ComponentLink<Self>) -> Self {
fn create(ctx: &Context<Self>) -> Self {
ProgressBarDoc {
callback: link.callback(|x| x),
callback: ctx.link().callback(|x| x),
state: ExampleProps {
intent: None,
animate: false,
@ -25,16 +25,12 @@ impl Component for ProgressBarDoc {
}
}
fn update(&mut self, msg: Self::Message) -> ShouldRender {
fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {
self.state = msg;
true
}
fn change(&mut self, _props: Self::Properties) -> ShouldRender {
true
}
fn view(&self) -> Html {
fn view(&self, _ctx: &Context<Self>) -> Html {
let example_props = self.state.clone();
let source = crate::include_raw_html!(
concat!(env!("OUT_DIR"), "/", file!(), ".html"),
@ -43,18 +39,18 @@ impl Component for ProgressBarDoc {
html! {
<div>
<H1 class=classes!("docs-title")>{"ProgressBar"}</H1>
<H1 class={classes!("docs-title")}>{"ProgressBar"}</H1>
<SourceCodeUrl />
<ExampleContainer
source=source
props=Some(html! {
source={source}
props={Some(html! {
<ProgressBarProps
callback={self.callback.clone()}
props=example_props.clone()
example_props={example_props.clone()}
/>
})
})}
>
<Example with example_props />
<Example ..example_props />
</ExampleContainer>
</div>
}
@ -63,26 +59,26 @@ impl Component for ProgressBarDoc {
crate::build_example_prop_component! {
ProgressBarProps for ExampleProps =>
fn view(&self) -> Html {
fn view(&self, ctx: &Context<Self>) -> Html {
html! {
<div>
<H5>{"Props"}</H5>
<div>
<Switch
onclick=self.update_props(|props, _| ExampleProps {
onclick={self.update_props(ctx.props(), |props, _| ExampleProps {
stripes: !props.stripes,
..props
})
checked=self.props.stripes
label=html!("Stripes")
})}
checked={ctx.props().example_props.stripes}
label={html!("Stripes")}
/>
<Switch
onclick=self.update_props(|props, _| ExampleProps {
onclick={self.update_props(ctx.props(), |props, _| ExampleProps {
animate: !props.animate,
..props
})
checked=self.props.animate
label=html!("Animate")
})}
checked={ctx.props().example_props.animate}
label={html!("Animate")}
/>
<p>{"Select intent:"}</p>
<HtmlSelect<Option<Intent>>
@ -93,10 +89,10 @@ crate::build_example_prop_component! {
(Some(Intent::Warning), "Warning".to_string()),
(Some(Intent::Danger), "Danger".to_string()),
]}
onchange=self.update_props(|props, intent| ExampleProps {
onchange={self.update_props(ctx.props(), |props, intent| ExampleProps {
intent,
..props
})
})}
/>
</div>
</div>

View file

@ -2,8 +2,6 @@ use yew::prelude::*;
use yewprint::{Label, Radio, RadioGroup};
pub struct Example {
props: ExampleProps,
link: ComponentLink<Self>,
selected_value: Lunch,
}
@ -22,15 +20,13 @@ impl Component for Example {
type Message = Msg;
type Properties = ExampleProps;
fn create(props: Self::Properties, link: ComponentLink<Self>) -> Self {
fn create(_ctx: &Context<Self>) -> Self {
Example {
props,
selected_value: Lunch::Salad,
link,
}
}
fn update(&mut self, msg: Self::Message) -> ShouldRender {
fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {
match msg {
Msg::ValueUpdate(value) => {
self.selected_value = value;
@ -39,51 +35,42 @@ impl Component for Example {
}
}
fn change(&mut self, props: Self::Properties) -> ShouldRender {
if self.props != props {
self.props = props;
true
} else {
false
}
}
fn view(&self) -> Html {
fn view(&self, ctx: &Context<Self>) -> Html {
html! {
<>
<div>
<Radio
label=html!("Blue pill")
inline=self.props.inline
disabled=self.props.disabled
large=self.props.large
name="group".to_string()
label={html!("Blue pill")}
inline={ctx.props().inline}
disabled={ctx.props().disabled}
large={ctx.props().large}
name={"group".to_string()}
/>
<Radio
label=html!("Red pill")
inline=self.props.inline
disabled=self.props.disabled
large=self.props.large
name="group".to_string()
label={html!("Red pill")}
inline={ctx.props().inline}
disabled={ctx.props().disabled}
large={ctx.props().large}
name={"group".to_string()}
/>
</div>
<div>
<RadioGroup<Lunch>
label=Some(html!(
label={Some(html!(
<Label>
{"Determine Lunch"}
</Label>
))
options=vec![
))}
options={vec![
(Lunch::Soup, "Soup".to_string()),
(Lunch::Salad, "Salad".to_string()),
(Lunch::Sandwich, "Sandwich".to_string()),
]
value=self.selected_value
onchange=self.link.callback(|v| Msg::ValueUpdate(v))
inline=self.props.inline
disabled=self.props.disabled
large=self.props.large
]}
value={self.selected_value}
onchange={ctx.link().callback(|v| Msg::ValueUpdate(v))}
inline={ctx.props().inline}
disabled={ctx.props().disabled}
large={ctx.props().large}
/>
</div>
</>

View file

@ -14,9 +14,9 @@ impl Component for RadioDoc {
type Message = ExampleProps;
type Properties = ();
fn create(_: Self::Properties, link: ComponentLink<Self>) -> Self {
fn create(ctx: &Context<Self>) -> Self {
RadioDoc {
callback: link.callback(|x| x),
callback: ctx.link().callback(|x| x),
state: ExampleProps {
disabled: false,
inline: false,
@ -25,16 +25,12 @@ impl Component for RadioDoc {
}
}
fn update(&mut self, msg: Self::Message) -> ShouldRender {
fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {
self.state = msg;
true
}
fn change(&mut self, _props: Self::Properties) -> ShouldRender {
true
}
fn view(&self) -> Html {
fn view(&self, _ctx: &Context<Self>) -> Html {
let example_props = self.state.clone();
let source = crate::include_raw_html!(
concat!(env!("OUT_DIR"), "/", file!(), ".html"),
@ -43,18 +39,18 @@ impl Component for RadioDoc {
html! {
<div>
<H1 class=classes!("docs-title")>{"Radio"}</H1>
<H1 class={classes!("docs-title")}>{"Radio"}</H1>
<SourceCodeUrl />
<ExampleContainer
source=source
props=Some(html! {
source={source}
props={Some(html! {
<RadioProps
callback={self.callback.clone()}
props=example_props.clone()
example_props={example_props.clone()}
/>
})
})}
>
<Example with example_props />
<Example ..example_props />
</ExampleContainer>
</div>
}
@ -63,33 +59,33 @@ impl Component for RadioDoc {
crate::build_example_prop_component! {
RadioProps for ExampleProps =>
fn view(&self) -> Html {
fn view(&self, ctx: &Context<Self>) -> Html {
html! {
<div>
<H5>{"Props"}</H5>
<Switch
onclick=self.update_props(|props, _| ExampleProps {
onclick={self.update_props(ctx.props(), |props, _| ExampleProps {
disabled: !props.disabled,
..props
})
checked=self.props.disabled
label=html!("Disabled")
})}
checked={ctx.props().example_props.disabled}
label={html!("Disabled")}
/>
<Switch
onclick=self.update_props(|props, _| ExampleProps {
onclick={self.update_props(ctx.props(), |props, _| ExampleProps {
inline: !props.inline,
..props
})
checked=self.props.inline
label=html!("Inline")
})}
checked={ctx.props().example_props.inline}
label={html!("Inline")}
/>
<Switch
onclick=self.update_props(|props, _| ExampleProps {
onclick={self.update_props(ctx.props(), |props, _| ExampleProps {
large: !props.large,
..props
})
checked=self.props.large
label=html!("Large")
})}
checked={ctx.props().example_props.large}
label={html!("Large")}
/>
</div>
}

View file

@ -3,11 +3,9 @@ use yew::prelude::*;
use yewprint::{Intent, Slider, Tag};
pub struct Example {
props: ExampleProps,
float: f64,
integer: i32,
log_level: Option<LogLevel>,
link: ComponentLink<Self>,
}
#[derive(Clone, PartialEq, Properties)]
@ -27,17 +25,15 @@ impl Component for Example {
type Message = Msg;
type Properties = ExampleProps;
fn create(props: Self::Properties, link: ComponentLink<Self>) -> Self {
fn create(_ctx: &Context<Self>) -> Self {
Example {
props,
float: 1.2,
integer: 30,
log_level: None,
link,
}
}
fn update(&mut self, msg: Self::Message) -> ShouldRender {
fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {
match msg {
Msg::FloatUpdate(value) => {
self.float = value;
@ -53,16 +49,7 @@ impl Component for Example {
true
}
fn change(&mut self, props: Self::Properties) -> ShouldRender {
if self.props != props {
self.props = props;
true
} else {
false
}
}
fn view(&self) -> Html {
fn view(&self, ctx: &Context<Self>) -> Html {
let percentage_labels = (0..=100)
.step_by(1)
.map(|x| (x, (x % 10 == 0).then(|| format!("{}%", x).into())))
@ -74,8 +61,8 @@ impl Component for Example {
style="display: flex; align-items: flex-start; width: 100%"
>
<Slider<f64>
selected=self.float
values=vec![
selected={self.float}
values={vec![
(0.0, Some("0".into())),
(0.1, None),
(0.2, None),
@ -87,43 +74,43 @@ impl Component for Example {
(0.8, None),
(0.9, None),
(1.0, Some("1".into())),
]
intent=self.props.intent
onchange=self.link.callback(|x| Msg::FloatUpdate(x))
]}
intent={ctx.props().intent}
onchange={ctx.link().callback(|x| Msg::FloatUpdate(x))}
/>
<Tag
style=Cow::Borrowed("width: 32px; margin-left: 16px")
style={"width: 32px; margin-left: 16px"}
minimal=true
intent=self.props.intent
intent={ctx.props().intent}
>
{format!("{:.1}", self.float)}
</Tag>
</div>
<Slider<i32>
values=percentage_labels
selected=self.integer
intent=self.props.intent
value_label=Cow::Owned(format!("{}%", self.integer))
onchange=self.link.callback(|x| Msg::IntegerUpdate(x))
values={percentage_labels}
selected={self.integer}
intent={ctx.props().intent}
value_label={Cow::Owned(format!("{}%", self.integer))}
onchange={ctx.link().callback(|x| Msg::IntegerUpdate(x))}
/>
<Slider<LogLevel>
values=vec![
values={vec![
(LogLevel::Off, Some("OFF".into())),
(LogLevel::Error, Some("ERROR".into())),
(LogLevel::Warn, Some("WARN".into())),
(LogLevel::Info, Some("INFO".into())),
(LogLevel::Debug, Some("DEBUG".into())),
(LogLevel::Trace, Some("TRACE".into())),
]
intent=self.props.intent
selected=self.log_level.clone()
onchange=self.link.callback(|x| Msg::LogLevelUpdate(x))
]}
intent={ctx.props().intent}
selected={self.log_level.clone()}
onchange={ctx.link().callback(|x| Msg::LogLevelUpdate(x))}
/>
<Slider<()>
values=vec![((), Some("Neo".into()))]
intent=self.props.intent
selected=()
onchange=self.link.callback(|_| Msg::Noop)
values={vec![((), Some("Neo".into()))]}
intent={ctx.props().intent}
selected={()}
onchange={ctx.link().callback(|_| Msg::Noop)}
/>
</>
}

View file

@ -14,9 +14,9 @@ impl Component for SliderDoc {
type Message = ExampleProps;
type Properties = ();
fn create(_: Self::Properties, link: ComponentLink<Self>) -> Self {
fn create(ctx: &Context<Self>) -> Self {
SliderDoc {
callback: link.callback(|x| x),
callback: ctx.link().callback(|x| x),
state: ExampleProps {
vertical: false,
intent: None,
@ -24,16 +24,12 @@ impl Component for SliderDoc {
}
}
fn update(&mut self, msg: Self::Message) -> ShouldRender {
fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {
self.state = msg;
true
}
fn change(&mut self, _props: Self::Properties) -> ShouldRender {
true
}
fn view(&self) -> Html {
fn view(&self, _ctx: &Context<Self>) -> Html {
let example_props = self.state.clone();
let source = crate::include_raw_html!(
concat!(env!("OUT_DIR"), "/", file!(), ".html"),
@ -42,21 +38,21 @@ impl Component for SliderDoc {
html! {
<div>
<H1 class=classes!("docs-title")>{"Slider"}</H1>
<H1 class={classes!("docs-title")}>{"Slider"}</H1>
<SourceCodeUrl />
<ExampleContainer
source=source
props=Some(html! {
source={source}
props={Some(html! {
<SliderProps
callback={self.callback.clone()}
props=example_props.clone()
example_props={example_props.clone()}
/>
})
})}
>
<Example with example_props />
<Example ..example_props />
</ExampleContainer>
<H2>{"Edge Cases"}</H2>
<div class=classes!("bp3-running-text")>
<div class={classes!("bp3-running-text")}>
<ul>
<li>
<p>
@ -108,17 +104,17 @@ impl Component for SliderDoc {
crate::build_example_prop_component! {
SliderProps for ExampleProps =>
fn view(&self) -> Html {
fn view(&self, ctx: &Context<Self>) -> Html {
html! {
<div>
<H5>{"Props"}</H5>
<Switch
onclick=self.update_props(|props, _| ExampleProps {
onclick={self.update_props(ctx.props(), |props, _| ExampleProps {
vertical: !props.vertical,
..props
})
checked=self.props.vertical
label=html!("Vertical")
})}
checked={ctx.props().example_props.vertical}
label={html!("Vertical")}
disabled=true
/>
<p>{"Select intent:"}</p>
@ -130,10 +126,10 @@ crate::build_example_prop_component! {
(Some(Intent::Warning), "Warning".to_string()),
(Some(Intent::Danger), "Danger".to_string()),
]}
onchange=self.update_props(|props, intent| ExampleProps {
onchange={self.update_props(ctx.props(), |props, intent| ExampleProps {
intent,
..props
})
})}
/>
</div>
}

View file

@ -1,45 +1,20 @@
use yew::prelude::*;
use yewprint::{Intent, Spinner};
pub struct Example {
props: ExampleProps,
}
#[derive(Clone, PartialEq, Properties)]
pub struct ExampleProps {
pub intent: Option<Intent>,
pub size: u32,
}
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 {
#[function_component(Example)]
pub fn example(props: &ExampleProps) -> Html {
html! {
<div>
<Spinner
size=self.props.size as f32
intent=self.props.intent
size={props.size as f32}
intent={props.intent}
/>
</div>
}
}
}

View file

@ -14,9 +14,9 @@ impl Component for SpinnerDoc {
type Message = ExampleProps;
type Properties = ();
fn create(_: Self::Properties, link: ComponentLink<Self>) -> Self {
fn create(ctx: &Context<Self>) -> Self {
SpinnerDoc {
callback: link.callback(|x| x),
callback: ctx.link().callback(|x| x),
state: ExampleProps {
intent: None,
size: 50,
@ -24,16 +24,12 @@ impl Component for SpinnerDoc {
}
}
fn update(&mut self, msg: Self::Message) -> ShouldRender {
fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {
self.state = msg;
true
}
fn change(&mut self, _props: Self::Properties) -> ShouldRender {
true
}
fn view(&self) -> Html {
fn view(&self, _ctx: &Context<Self>) -> Html {
let example_props = self.state.clone();
let source = crate::include_raw_html!(
concat!(env!("OUT_DIR"), "/", file!(), ".html"),
@ -42,19 +38,19 @@ impl Component for SpinnerDoc {
html! {
<div>
<H1 class=classes!("docs-title")>{"Spinner"}</H1>
<H1 class={classes!("docs-title")}>{"Spinner"}</H1>
<SourceCodeUrl />
<div>
<ExampleContainer
source=source
props=Some(html! {
source={source}
props={Some(html! {
<SpinnerProps
callback={self.callback.clone()}
props=example_props.clone()
example_props={example_props.clone()}
/>
})
})}
>
<Example with example_props />
<Example ..example_props />
</ExampleContainer>
</div>
</div>
@ -64,7 +60,7 @@ impl Component for SpinnerDoc {
crate::build_example_prop_component! {
SpinnerProps for ExampleProps =>
fn view(&self) -> Html {
fn view(&self, ctx: &Context<Self>) -> Html {
html! {
<div>
<H5>{"Props"}</H5>
@ -78,10 +74,10 @@ crate::build_example_prop_component! {
(Some(Intent::Warning), "Warning".to_string()),
(Some(Intent::Danger), "Danger".to_string()),
]}
onchange=self.update_props(|props, intent| ExampleProps {
onchange={self.update_props(ctx.props(), |props, intent| ExampleProps {
intent,
..props
})
})}
/>
<p
style="margin-top: 5px;"
@ -89,7 +85,7 @@ crate::build_example_prop_component! {
{"Select Size:"}
</p>
<Slider<u32>
selected=self.props.size
selected={ctx.props().example_props.size}
values={vec![
(10, Some("10".into())),
(20, None),
@ -102,10 +98,10 @@ crate::build_example_prop_component! {
(90, None),
(100, Some("100".into())),
]}
onchange=self.update_props(|props, size| ExampleProps {
onchange={self.update_props(ctx.props(), |props, size| ExampleProps {
size,
..props
})
})}
/>
</div>
</div>

View file

@ -1,10 +1,6 @@
use yew::prelude::*;
use yewprint::{Label, Switch};
pub struct Example {
props: ExampleProps,
}
#[derive(Clone, PartialEq, Properties)]
pub struct ExampleProps {
pub disabled: bool,
@ -13,63 +9,41 @@ pub struct ExampleProps {
pub align_right: 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 {
#[function_component(Example)]
pub fn example(props: &ExampleProps) -> Html {
html! {
<div>
<Label>{"Privacy settings"}</Label>
<Switch
disabled=self.props.disabled
inline=self.props.inline
large=self.props.large
label=html!{<strong>{"Enabled"}</strong>}
align_right=self.props.align_right
disabled={props.disabled}
inline={props.inline}
large={props.large}
align_right={props.align_right}
label={html!("Enabled")}
/>
<Switch
disabled=self.props.disabled
inline=self.props.inline
large=self.props.large
label=html!{<em>{"Public"}</em>}
align_right=self.props.align_right
disabled={props.disabled}
inline={props.inline}
large={props.large}
align_right={props.align_right}
label={html!(<em>{"Public"}</em>)}
/>
<Switch
disabled=self.props.disabled
inline=self.props.inline
large=self.props.large
checked=true
label=html!{<u>{"Cooperative"}</u>}
align_right=self.props.align_right
disabled={props.disabled}
inline={props.inline}
large={props.large}
align_right={props.align_right}
label={html!(<strong>{"Cooperative"}</strong>)}
/>
<Switch
disabled=self.props.disabled
inline=self.props.inline
large=self.props.large
label=html!{"Containing Text"}
disabled={props.disabled}
inline={props.inline}
large={props.large}
align_right={props.align_right}
label={html!(<u>{"Containing Text"}</u>)}
inner_label_checked={"on".to_string()}
inner_label={"off".to_string()}
align_right=self.props.align_right
/>
</div>
}
}
}

View file

@ -14,9 +14,9 @@ impl Component for SwitchDoc {
type Message = ExampleProps;
type Properties = ();
fn create(_: Self::Properties, link: ComponentLink<Self>) -> Self {
fn create(ctx: &Context<Self>) -> Self {
SwitchDoc {
callback: link.callback(|x| x),
callback: ctx.link().callback(|x| x),
state: ExampleProps {
disabled: false,
inline: false,
@ -26,16 +26,12 @@ impl Component for SwitchDoc {
}
}
fn update(&mut self, msg: Self::Message) -> ShouldRender {
fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {
self.state = msg;
true
}
fn change(&mut self, _props: Self::Properties) -> ShouldRender {
true
}
fn view(&self) -> Html {
fn view(&self, _ctx: &Context<Self>) -> Html {
let example_props = self.state.clone();
let source = crate::include_raw_html!(
concat!(env!("OUT_DIR"), "/", file!(), ".html"),
@ -44,19 +40,19 @@ impl Component for SwitchDoc {
html! {
<div>
<H1 class=classes!("docs-title")>{"Switch"}</H1>
<H1 class={classes!("docs-title")}>{"Switch"}</H1>
<SourceCodeUrl />
<ExampleContainer
source=source
props=Some(html! {
source={source}
props={Some(html! {
<SwitchProps
callback={self.callback.clone()}
props=example_props.clone()
example_props={example_props.clone()}
>
</SwitchProps>
})
})}
>
<Example with example_props />
<Example ..example_props />
</ExampleContainer>
</div>
}
@ -65,41 +61,41 @@ impl Component for SwitchDoc {
crate::build_example_prop_component! {
SwitchProps for ExampleProps =>
fn view(&self) -> Html {
fn view(&self, ctx: &Context<Self>) -> Html {
html! {
<div>
<H5>{"Props"}</H5>
<Switch
onclick=self.update_props(|props, _| ExampleProps {
onclick={self.update_props(ctx.props(), |props, _| ExampleProps {
disabled: !props.disabled,
..props
})
checked=self.props.disabled
label=html!("Disabled")
})}
checked={ctx.props().example_props.disabled}
label={html!("Disabled")}
/>
<Switch
onclick=self.update_props(|props, _| ExampleProps {
onclick={self.update_props(ctx.props(), |props, _| ExampleProps {
inline: !props.inline,
..props
})
checked=self.props.inline
label=html!("Inline")
})}
checked={ctx.props().example_props.inline}
label={html!("Inline")}
/>
<Switch
onclick=self.update_props(|props, _| ExampleProps {
onclick={self.update_props(ctx.props(), |props, _| ExampleProps {
large: !props.large,
..props
})
checked=self.props.large
label=html!("Large")
})}
checked={ctx.props().example_props.large}
label={html!("Large")}
/>
<Switch
onclick=self.update_props(|props, _| ExampleProps {
onclick={self.update_props(ctx.props(), |props, _| ExampleProps {
align_right: !props.align_right,
..props
})
checked=self.props.align_right
label=html!("Align right")
})}
checked={ctx.props().example_props.align_right}
label={html!("Align right")}
/>
</div>
}

View file

@ -2,8 +2,6 @@ use yew::prelude::*;
use yewprint::{Tab, Tabs};
pub struct Example {
link: ComponentLink<Self>,
props: ExampleProps,
selected: Civilization,
}
@ -17,15 +15,13 @@ impl Component for Example {
type Message = Civilization;
type Properties = ExampleProps;
fn create(props: Self::Properties, link: ComponentLink<Self>) -> Self {
fn create(_ctx: &Context<Self>) -> Self {
Example {
link,
props,
selected: Civilization::Minoan,
}
}
fn update(&mut self, msg: Self::Message) -> ShouldRender {
fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {
if self.selected != msg {
self.selected = msg;
true
@ -34,25 +30,16 @@ impl Component for Example {
}
}
fn change(&mut self, props: Self::Properties) -> ShouldRender {
if self.props != props {
self.props = props;
true
} else {
false
}
}
fn view(&self) -> Html {
fn view(&self, ctx: &Context<Self>) -> Html {
html! {
<div>
<Tabs<Civilization>
id="civilizations"
animate=self.props.animate
vertical=self.props.vertical
selected_tab_id=self.selected
onchange=self.link.callback(|x| x)
tabs=vec![
animate={ctx.props().animate}
vertical={ctx.props().vertical}
selected_tab_id={self.selected}
onchange={ctx.link().callback(|x| x)}
tabs={vec![
Tab {
disabled: false,
id: Civilization::Sumer,
@ -133,7 +120,7 @@ impl Component for Example {
panel_class: Classes::default(),
title_class: Classes::default(),
},
]
]}
/>
</div>
}

View file

@ -14,9 +14,9 @@ impl Component for TabsDoc {
type Message = ExampleProps;
type Properties = ();
fn create(_: Self::Properties, link: ComponentLink<Self>) -> Self {
fn create(ctx: &Context<Self>) -> Self {
TabsDoc {
callback: link.callback(|x| x),
callback: ctx.link().callback(|x| x),
state: ExampleProps {
animate: true,
vertical: false,
@ -24,16 +24,12 @@ impl Component for TabsDoc {
}
}
fn update(&mut self, msg: Self::Message) -> ShouldRender {
fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {
self.state = msg;
true
}
fn change(&mut self, _props: Self::Properties) -> ShouldRender {
true
}
fn view(&self) -> Html {
fn view(&self, _ctx: &Context<Self>) -> Html {
let example_props = self.state.clone();
let source = crate::include_raw_html!(
concat!(env!("OUT_DIR"), "/", file!(), ".html"),
@ -42,19 +38,19 @@ impl Component for TabsDoc {
html! {
<div>
<H1 class=classes!("docs-title")>{"Tabs"}</H1>
<H1 class={classes!("docs-title")}>{"Tabs"}</H1>
<SourceCodeUrl />
<div>
<ExampleContainer
source=source
props=Some(html! {
source={source}
props={Some(html! {
<TabsProps
callback={self.callback.clone()}
props=example_props.clone()
example_props={example_props.clone()}
/>
})
})}
>
<Example with example_props />
<Example ..example_props />
</ExampleContainer>
</div>
</div>
@ -64,25 +60,25 @@ impl Component for TabsDoc {
crate::build_example_prop_component! {
TabsProps for ExampleProps =>
fn view(&self) -> Html {
fn view(&self, ctx: &Context<Self>) -> Html {
html! {
<div>
<H5>{"Props"}</H5>
<Switch
onclick=self.update_props(|props, _| ExampleProps {
onclick={self.update_props(ctx.props(), |props, _| ExampleProps {
animate: !props.animate,
..props
})
checked=self.props.animate
label=html!("Animate indicator")
})}
checked={ctx.props().example_props.animate}
label={html!("Animate indicator")}
/>
<Switch
onclick=self.update_props(|props, _| ExampleProps {
onclick={self.update_props(ctx.props(), |props, _| ExampleProps {
vertical: !props.vertical,
..props
})
checked=self.props.vertical
label=html!("Use vertical tabs")
})}
checked={ctx.props().example_props.vertical}
label={html!("Use vertical tabs")}
/>
</div>
}

View file

@ -2,8 +2,6 @@ use yew::prelude::*;
use yewprint::{IconName, Intent, Tag};
pub struct Example {
props: ExampleProps,
link: ComponentLink<Self>,
tags: Vec<String>,
}
@ -33,12 +31,12 @@ 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 create(ctx: &Context<Self>) -> Self {
let tags = ctx.props().initial_tags.clone();
Example { tags }
}
fn update(&mut self, msg: Self::Message) -> ShouldRender {
fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {
match msg {
ExampleMsg::Remove(label) => {
self.tags = self
@ -53,43 +51,38 @@ impl Component for Example {
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();
fn changed(&mut self, ctx: &Context<Self>) -> bool {
if ctx.props().reset_tags != ctx.props().reset_tags {
self.tags = ctx.props().initial_tags.clone();
}
self.props = props;
true
} else {
false
}
}
fn view(&self) -> Html {
fn view(&self, ctx: &Context<Self>) -> Html {
self.tags
.iter()
.map(|label| {
let remove = {
let label = label.clone();
self.props.removable.then(|| {
self.link
ctx.props().removable.then(|| {
ctx.link()
.callback(move |_| ExampleMsg::Remove(label.clone()))
})
};
html! {
<Tag
active=self.props.active
fill=self.props.fill
icon=self.props.icon.then(|| 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.then(|| IconName::Star)
round=self.props.round
onremove=remove
onclick=self.link.callback(|_| ExampleMsg::Click)
active={ctx.props().active}
fill={ctx.props().fill}
icon={ctx.props().icon.then(|| IconName::Print)}
intent={ctx.props().intent}
interactive={ctx.props().interactive}
large={ctx.props().large}
minimal={ctx.props().minimal}
multiline={ctx.props().multiline}
right_icon={ctx.props().right_icon.then(|| IconName::Star)}
round={ctx.props().round}
onremove={remove}
onclick={ctx.link().callback(|_| ExampleMsg::Click)}
>
{label.clone()}
</Tag>

View file

@ -28,9 +28,9 @@ impl Component for TagDoc {
type Message = ExampleProps;
type Properties = ();
fn create(_props: Self::Properties, link: ComponentLink<Self>) -> Self {
fn create(ctx: &Context<Self>) -> Self {
TagDoc {
callback: link.callback(|x| x),
callback: ctx.link().callback(|x| x),
state: ExampleProps {
initial_tags: initial_tags(),
active: false,
@ -49,16 +49,12 @@ impl Component for TagDoc {
}
}
fn update(&mut self, msg: Self::Message) -> ShouldRender {
fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {
self.state = msg;
true
}
fn change(&mut self, _props: Self::Properties) -> ShouldRender {
true
}
fn view(&self) -> Html {
fn view(&self, _ctx: &Context<Self>) -> Html {
let example_props = self.state.clone();
let source = crate::include_raw_html!(
concat!(env!("OUT_DIR"), "/", file!(), ".html"),
@ -67,18 +63,18 @@ impl Component for TagDoc {
html! {
<div>
<H1 class=classes!("docs-title")>{"Tag"}</H1>
<H1 class={classes!("docs-title")}>{"Tag"}</H1>
<SourceCodeUrl />
<ExampleContainer
source=source
props=Some(html!{
source={source}
props={Some(html!{
<TagProps
callback={self.callback.clone()}
props=example_props.clone()
example_props={example_props.clone()}
/>
})
})}
>
<Example with example_props/>
<Example ..example_props/>
</ExampleContainer>
</div>
}
@ -87,90 +83,92 @@ impl Component for TagDoc {
crate::build_example_prop_component! {
TagProps for ExampleProps =>
fn view(&self) -> Html {
fn view(&self, ctx: &Context<Self>) -> Html {
let props = ctx.props();
html! {
<div>
<H5>{"Props"}</H5>
<div>
<Switch
onclick=self.update_props(|props, _| ExampleProps {
onclick={self.update_props(props, |props, _| ExampleProps {
active: !props.active,
..props
})
checked=self.props.active
label=html!("Active")
})}
checked={props.example_props.active}
label={html!("Active")}
/>
<Switch
onclick=self.update_props(|props, _| ExampleProps {
onclick={self.update_props(props, |props, _| ExampleProps {
fill: !props.fill,
..props
})
checked=self.props.fill
label=html!("Fill")
})}
checked={props.example_props.fill}
label={html!("Fill")}
/>
<Switch
onclick=self.update_props(|props, _| ExampleProps {
onclick={self.update_props(props, |props, _| ExampleProps {
interactive: !props.interactive,
..props
})
checked=self.props.interactive
label=html!("Interactive")
})}
checked={props.example_props.interactive}
label={html!("Interactive")}
/>
<Switch
onclick=self.update_props(|props, _| ExampleProps {
onclick={self.update_props(props, |props, _| ExampleProps {
large: !props.large,
..props
})
checked=self.props.large
label=html!("Large")
})}
checked={props.example_props.large}
label={html!("Large")}
/>
<Switch
onclick=self.update_props(|props, _| ExampleProps {
onclick={self.update_props(props, |props, _| ExampleProps {
minimal: !props.minimal,
..props
})
checked=self.props.minimal
label=html!("Minimal")
})}
checked={props.example_props.minimal}
label={html!("Minimal")}
/>
<Switch
onclick=self.update_props(|props, _| ExampleProps {
onclick={self.update_props(props, |props, _| ExampleProps {
multiline: !props.multiline,
..props
})
checked=self.props.multiline
label=html!("Multiline")
})}
checked={props.example_props.multiline}
label={html!("Multiline")}
/>
<Switch
onclick=self.update_props(|props, _| ExampleProps {
onclick={self.update_props(props, |props, _| ExampleProps {
round: !props.round,
..props
})
checked=self.props.round
label=html!("Round")
})}
checked={props.example_props.round}
label={html!("Round")}
/>
<Switch
onclick=self.update_props(|props, _| ExampleProps {
onclick={self.update_props(props, |props, _| ExampleProps {
removable: !props.removable,
..props
})
checked=self.props.removable
label=html!("Removable")
})}
checked={props.example_props.removable}
label={html!("Removable")}
/>
<Switch
onclick=self.update_props(|props, _| ExampleProps {
onclick={self.update_props(props, |props, _| ExampleProps {
icon: !props.icon,
..props
})
checked=self.props.icon
label=html!("Icon")
})}
checked={props.example_props.icon}
label={html!("Icon")}
/>
<Switch
onclick=self.update_props(|props, _| ExampleProps {
onclick={self.update_props(props, |props, _| ExampleProps {
right_icon: !props.right_icon,
..props
})
checked=self.props.right_icon
label=html!("Right icon")
})}
checked={props.example_props.right_icon}
label={html!("Right icon")}
/>
<p>{"Select intent:"}</p>
<ButtonGroup
@ -184,17 +182,17 @@ crate::build_example_prop_component! {
(Some(Intent::Warning), "Warning".to_string()),
(Some(Intent::Danger), "Danger".to_string()),
]}
onchange=self.update_props(|props, intent| ExampleProps {
onchange={self.update_props(props, |props, intent| ExampleProps {
intent,
..props
})
})}
/>
<Button
icon=IconName::Refresh
onclick=self.update_props(|props, _| ExampleProps {
icon={IconName::Refresh}
onclick={self.update_props(props, |props, _| ExampleProps {
reset_tags: props.reset_tags + 1,
..props
})
..props.clone()
})}
>
{"Reset tags"}
</Button>

View file

@ -1,44 +1,19 @@
use yew::prelude::*;
use yewprint::Text;
pub struct Example {
props: ExampleProps,
}
#[derive(Clone, PartialEq, Properties)]
pub struct ExampleProps {
pub ellipsize: bool,
pub text: String,
}
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 {
#[function_component(Example)]
pub fn example(props: &ExampleProps) -> Html {
html! {
<div style="width: 150px; height: 20px">
<Text ellipsize=self.props.ellipsize>
{&self.props.text}
<Text ellipsize={props.ellipsize}>
{&props.text}
</Text>
</div>
}
}
}

View file

@ -2,6 +2,7 @@ mod example;
use crate::ExampleContainer;
use example::*;
use web_sys::HtmlInputElement;
use yew::prelude::*;
use yewprint::{Switch, H1, H5};
@ -14,9 +15,9 @@ impl Component for TextDoc {
type Message = ExampleProps;
type Properties = ();
fn create(_: Self::Properties, link: ComponentLink<Self>) -> Self {
fn create(ctx: &Context<Self>) -> Self {
TextDoc {
callback: link.callback(|x| x),
callback: ctx.link().callback(|x| x),
state: ExampleProps {
ellipsize: false,
text: String::from("Hello, world!"),
@ -24,16 +25,12 @@ impl Component for TextDoc {
}
}
fn update(&mut self, msg: Self::Message) -> ShouldRender {
fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {
self.state = msg;
true
}
fn change(&mut self, _props: Self::Properties) -> ShouldRender {
true
}
fn view(&self) -> Html {
fn view(&self, _ctx: &Context<Self>) -> Html {
let example_props = self.state.clone();
let source = crate::include_raw_html!(
concat!(env!("OUT_DIR"), "/", file!(), ".html"),
@ -42,19 +39,19 @@ impl Component for TextDoc {
html! {
<div>
<H1 class=classes!("docs-title")>{"Text"}</H1>
<H1 class={classes!("docs-title")}>{"Text"}</H1>
<SourceCodeUrl />
<div>
<ExampleContainer
source=source
props=Some(html! {
source={source}
props={Some(html! {
<TextProps
callback={self.callback.clone()}
props=example_props.clone()
example_props={example_props.clone()}
/>
})
})}
>
<Example with example_props />
<Example ..example_props />
</ExampleContainer>
</div>
</div>
@ -64,37 +61,36 @@ impl Component for TextDoc {
crate::build_example_prop_component! {
TextProps for ExampleProps =>
fn view(&self) -> Html {
fn view(&self, ctx: &Context<Self>) -> Html {
html! {
<div>
<H5>{"Props"}</H5>
<Switch
onclick=self.update_props(|props, _| ExampleProps {
onclick={self.update_props(ctx.props(), |props, _| ExampleProps {
ellipsize: !props.ellipsize,
..props
})
checked=self.props.ellipsize
label=html!("Ellipsize")
})}
checked={ctx.props().example_props.ellipsize}
label={html!("Ellipsize")}
/>
<input
class="bp3-input"
onchange=self.update_props(|props, e|
match e {
ChangeData::Value(text) => {
onchange={self.update_props(ctx.props(), |props, e: Event| {
if let Some(input) = e.target_dyn_into::<HtmlInputElement>() {
ExampleProps {
text,
text: input.value(),
..props
}
},
_ => {
} else {
ExampleProps {
text: "Hello, world!".to_string(),
..props
}
}
})
}
)}
type="text"
value={self.props.text.clone()}
value={ctx.props().example_props.text.clone()}
/>
</div>
}

View file

@ -1,10 +1,6 @@
use yew::prelude::*;
use yewprint::{Intent, TextArea};
pub struct Example {
props: ExampleProps,
}
#[derive(Clone, PartialEq, Properties)]
pub struct ExampleProps {
pub intent: Option<Intent>,
@ -13,36 +9,15 @@ pub struct ExampleProps {
pub fill: 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 {
#[function_component(Example)]
pub fn example(props: &ExampleProps) -> Html {
html! {
<div style="width: 200px; height: 50px">
<TextArea intent=self.props.intent
large=self.props.large
fill=self.props.fill
small=self.props.small
<TextArea intent={props.intent}
large={props.large}
fill={props.fill}
small={props.small}
/>
</div>
}
}
}

View file

@ -14,9 +14,9 @@ impl Component for TextAreaDoc {
type Message = ExampleProps;
type Properties = ();
fn create(_: Self::Properties, link: ComponentLink<Self>) -> Self {
fn create(ctx: &Context<Self>) -> Self {
TextAreaDoc {
callback: link.callback(|x| x),
callback: ctx.link().callback(|x| x),
state: ExampleProps {
intent: None,
large: false,
@ -26,16 +26,12 @@ impl Component for TextAreaDoc {
}
}
fn update(&mut self, msg: Self::Message) -> ShouldRender {
fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {
self.state = msg;
true
}
fn change(&mut self, _props: Self::Properties) -> ShouldRender {
true
}
fn view(&self) -> Html {
fn view(&self, _ctx: &Context<Self>) -> Html {
let example_props = self.state.clone();
let source = crate::include_raw_html!(
concat!(env!("OUT_DIR"), "/", file!(), ".html"),
@ -44,19 +40,19 @@ impl Component for TextAreaDoc {
html! {
<div>
<H1 class=classes!("docs-title")>{"Text"}</H1>
<H1 class={classes!("docs-title")}>{"Text"}</H1>
<SourceCodeUrl />
<div>
<ExampleContainer
source=source
props=Some(html! {
source={source}
props={Some(html! {
<TextAreaProps
callback={self.callback.clone()}
props=example_props.clone()
example_props={example_props.clone()}
/>
})
})}
>
<Example with example_props />
<Example ..example_props />
</ExampleContainer>
</div>
</div>
@ -66,33 +62,33 @@ impl Component for TextAreaDoc {
crate::build_example_prop_component! {
TextAreaProps for ExampleProps =>
fn view(&self) -> Html {
fn view(&self, ctx: &Context<Self>) -> Html {
html! {
<div>
<H5>{"Props"}</H5>
<Switch
onclick=self.update_props(|props, _| ExampleProps {
onclick={self.update_props(ctx.props(), |props, _| ExampleProps {
fill: !props.fill,
..props
})
checked=self.props.fill
label=html!("Fill")
})}
checked={ctx.props().example_props.fill}
label={html!("Fill")}
/>
<Switch
onclick=self.update_props(|props, _| ExampleProps {
onclick={self.update_props(ctx.props(), |props, _| ExampleProps {
large: !props.large,
..props
})
checked=self.props.large
label=html!("Large")
})}
checked={ctx.props().example_props.large}
label={html!("Large")}
/>
<Switch
onclick=self.update_props(|props, _| ExampleProps {
onclick={self.update_props(ctx.props(), |props, _| ExampleProps {
small: !props.small,
..props
})
checked=self.props.small
label=html!("Small")
})}
checked={ctx.props().example_props.small}
label={html!("Small")}
/>
<HtmlSelect<Option<Intent>>
options={vec![
@ -102,10 +98,10 @@ crate::build_example_prop_component! {
(Some(Intent::Warning), "Warning".to_string()),
(Some(Intent::Danger), "Danger".to_string()),
]}
onchange=self.update_props(|props, intent| ExampleProps {
onchange={self.update_props(ctx.props(), |props, intent| ExampleProps {
intent,
..props
})
})}
/>
</div>
}

View file

@ -17,7 +17,7 @@ impl Component for Example {
type Message = Msg;
type Properties = ();
fn create(_props: Self::Properties, link: ComponentLink<Self>) -> Self {
fn create(ctx: &Context<Self>) -> Self {
let mut tree = TreeBuilder::new().build();
let root_id = tree
.insert(
@ -71,7 +71,7 @@ impl Component for Example {
icon: Some(IconName::Tag),
icon_intent: Some(Intent::Primary),
label: "Outer file".into(),
secondary_label: Some(html!(<Icon icon=IconName::EyeOpen />)),
secondary_label: Some(html!(<Icon icon={IconName::EyeOpen} />)),
data: 3,
..Default::default()
}),
@ -81,12 +81,12 @@ impl Component for Example {
Self {
tree: tree.into(),
callback_expand_node: link.callback(|(node_id, _)| Msg::ExpandNode(node_id)),
callback_select_node: link.callback(|(node_id, _)| Msg::SelectNode(node_id)),
callback_expand_node: ctx.link().callback(|(node_id, _)| Msg::ExpandNode(node_id)),
callback_select_node: ctx.link().callback(|(node_id, _)| Msg::SelectNode(node_id)),
}
}
fn update(&mut self, msg: Self::Message) -> ShouldRender {
fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {
match msg {
Msg::ExpandNode(node_id) => {
let mut tree = self.tree.borrow_mut();
@ -109,17 +109,13 @@ impl Component for Example {
true
}
fn change(&mut self, _props: Self::Properties) -> ShouldRender {
true
}
fn view(&self) -> Html {
fn view(&self, _ctx: &Context<Self>) -> Html {
html! {
<Tree<i32>
tree=self.tree.clone()
on_collapse=Some(self.callback_expand_node.clone())
on_expand=Some(self.callback_expand_node.clone())
onclick=Some(self.callback_select_node.clone())
tree={self.tree.clone()}
on_collapse={Some(self.callback_expand_node.clone())}
on_expand={Some(self.callback_expand_node.clone())}
onclick={Some(self.callback_select_node.clone())}
/>
}
}

View file

@ -11,19 +11,15 @@ impl Component for TreeDoc {
type Message = ();
type Properties = ();
fn create(_: Self::Properties, _link: ComponentLink<Self>) -> Self {
fn create(_ctx: &Context<Self>) -> Self {
TreeDoc
}
fn update(&mut self, _msg: Self::Message) -> ShouldRender {
fn update(&mut self, _ctx: &Context<Self>, _msg: Self::Message) -> bool {
true
}
fn change(&mut self, _props: Self::Properties) -> ShouldRender {
true
}
fn view(&self) -> Html {
fn view(&self, _ctx: &Context<Self>) -> Html {
let source = crate::include_raw_html!(
concat!(env!("OUT_DIR"), "/", file!(), ".html"),
"bp3-code-block"
@ -31,9 +27,9 @@ impl Component for TreeDoc {
html! {
<div>
<H1 class=classes!("docs-title")>{"Tree"}</H1>
<H1 class={classes!("docs-title")}>{"Tree"}</H1>
<SourceCodeUrl />
<ExampleContainer source=source>
<ExampleContainer source={source}>
<Example />
</ExampleContainer>
</div>