mirror of
https://github.com/yewprint/yewprint
synced 2024-11-22 03:23:03 +00:00
Update dependencies (#154)
Co-authored-by: Cecile Tonglet <cecile.tonglet@cecton.com>
This commit is contained in:
parent
70aa7a0736
commit
3e58abab37
59 changed files with 1781 additions and 1038 deletions
1301
Cargo.lock
generated
1301
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -18,14 +18,15 @@ default = ["tree"]
|
||||||
tree = ["id_tree"]
|
tree = ["id_tree"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
gloo = "0.6"
|
gloo = "0.8"
|
||||||
id_tree = { version = "1.7", optional = true }
|
id_tree = { version = "1.8", optional = true }
|
||||||
|
implicit-clone = "0.3.5"
|
||||||
wasm-bindgen = "0.2"
|
wasm-bindgen = "0.2"
|
||||||
web-sys = { version = "0.3", features = ["DomRect", "Element", "Event", "HtmlSelectElement"] }
|
web-sys = { version = "0.3", features = ["DomRect", "Element", "Event", "HtmlSelectElement"] }
|
||||||
yew = "0.19"
|
yew = "0.20"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
heck = "0.3"
|
heck = "0.4"
|
||||||
regex = { version = "1", default-features = false, features = ["std", "unicode-perl"] }
|
regex = { version = "1", default-features = false, features = ["std", "unicode-perl"] }
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
|
|
4
build.rs
4
build.rs
|
@ -7,7 +7,7 @@
|
||||||
//! After that you can extract the file following this path:
|
//! After that you can extract the file following this path:
|
||||||
//! package/lib/esnext/generated/iconSvgPaths.js
|
//! package/lib/esnext/generated/iconSvgPaths.js
|
||||||
|
|
||||||
use heck::CamelCase;
|
use heck::ToUpperCamelCase;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::env;
|
use std::env;
|
||||||
|
@ -29,7 +29,7 @@ fn main() {
|
||||||
src.push_str(&map[1]);
|
src.push_str(&map[1]);
|
||||||
src.push_str("(icon: IconName) -> &'static [&'static str] { match icon {\n");
|
src.push_str("(icon: IconName) -> &'static [&'static str] { match icon {\n");
|
||||||
for item in re_item.captures_iter(&map[2]) {
|
for item in re_item.captures_iter(&map[2]) {
|
||||||
let key = item[1].to_camel_case();
|
let key = item[1].to_upper_camel_case();
|
||||||
src.push_str("IconName::");
|
src.push_str("IconName::");
|
||||||
src.push_str(&key);
|
src.push_str(&key);
|
||||||
src.push_str(" => &");
|
src.push_str(" => &");
|
||||||
|
|
|
@ -14,26 +14,36 @@ pub struct ButtonGroupProps {
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
pub style: Option<AttrValue>,
|
pub style: Option<AttrValue>,
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
pub children: html::Children,
|
pub children: Children,
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
pub class: Classes,
|
pub class: Classes,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[function_component(ButtonGroup)]
|
#[function_component(ButtonGroup)]
|
||||||
pub fn button_group(props: &ButtonGroupProps) -> Html {
|
pub fn button_group(props: &ButtonGroupProps) -> Html {
|
||||||
|
let ButtonGroupProps {
|
||||||
|
minimal,
|
||||||
|
vertical,
|
||||||
|
fill,
|
||||||
|
large,
|
||||||
|
style,
|
||||||
|
children,
|
||||||
|
class,
|
||||||
|
} = props;
|
||||||
|
|
||||||
html! {
|
html! {
|
||||||
<div
|
<div
|
||||||
class={classes!(
|
class={classes!(
|
||||||
"bp3-button-group",
|
"bp3-button-group",
|
||||||
props.minimal.then_some("bp3-minimal"),
|
minimal.then_some("bp3-minimal"),
|
||||||
props.fill.then_some("bp3-fill"),
|
fill.then_some("bp3-fill"),
|
||||||
props.large.then_some("bp3-large"),
|
large.then_some("bp3-large"),
|
||||||
props.vertical.then_some("bp3-vertical"),
|
vertical.then_some("bp3-vertical"),
|
||||||
props.class.clone(),
|
class.clone(),
|
||||||
)}
|
)}
|
||||||
style={props.style.clone()}
|
{style}
|
||||||
>
|
>
|
||||||
{props.children.clone()}
|
{children.clone()}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,59 +33,65 @@ pub struct ButtonProps {
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
pub style: Option<AttrValue>,
|
pub style: Option<AttrValue>,
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
pub children: html::Children,
|
pub children: Children,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[function_component(Button)]
|
#[function_component(Button)]
|
||||||
pub fn button(props: &ButtonProps) -> Html {
|
pub fn button(props: &ButtonProps) -> Html {
|
||||||
|
let ButtonProps {
|
||||||
|
fill,
|
||||||
|
minimal,
|
||||||
|
small,
|
||||||
|
outlined,
|
||||||
|
loading,
|
||||||
|
large,
|
||||||
|
active,
|
||||||
|
disabled,
|
||||||
|
icon,
|
||||||
|
intent,
|
||||||
|
title,
|
||||||
|
onclick,
|
||||||
|
class,
|
||||||
|
style,
|
||||||
|
children,
|
||||||
|
} = props;
|
||||||
|
|
||||||
html! {
|
html! {
|
||||||
<button
|
<button
|
||||||
class={classes!(
|
class={classes!(
|
||||||
"bp3-button",
|
"bp3-button",
|
||||||
props.fill.then_some("bp3-fill"),
|
fill.then_some("bp3-fill"),
|
||||||
props.minimal.then_some("bp3-minimal"),
|
minimal.then_some("bp3-minimal"),
|
||||||
props.small.then_some("bp3-small"),
|
small.then_some("bp3-small"),
|
||||||
props.outlined.then_some("bp3-outlined"),
|
outlined.then_some("bp3-outlined"),
|
||||||
props.loading.then_some("bp3-loading"),
|
loading.then_some("bp3-loading"),
|
||||||
props.large.then_some("bp3-large"),
|
large.then_some("bp3-large"),
|
||||||
(props.active && !props.disabled).then_some("bp3-active"),
|
(*active && !disabled).then_some("bp3-active"),
|
||||||
props.disabled.then_some("bp3-disabled"),
|
disabled.then_some("bp3-disabled"),
|
||||||
props.intent,
|
intent,
|
||||||
props.class.clone(),
|
class.clone(),
|
||||||
)}
|
)}
|
||||||
style={props.style.clone()}
|
{style}
|
||||||
onclick={(!props.disabled).then_some(props.onclick.clone())}
|
{title}
|
||||||
|
onclick={(!disabled).then_some(onclick.clone())}
|
||||||
>
|
>
|
||||||
{
|
{
|
||||||
props
|
loading
|
||||||
.loading
|
|
||||||
.then(|| html! {
|
.then(|| html! {
|
||||||
<Spinner
|
<Spinner
|
||||||
class={classes!("bp3-button-spinner")}
|
class={classes!("bp3-button-spinner")}
|
||||||
size={ICON_SIZE_LARGE as f32}
|
size={ICON_SIZE_LARGE as f32}
|
||||||
/>
|
/>
|
||||||
})
|
})
|
||||||
.unwrap_or_default()
|
|
||||||
}
|
}
|
||||||
|
{icon.map(|icon| html!(<Icon {icon} />))}
|
||||||
{
|
{
|
||||||
if let Some(icon) = props.icon {
|
(!children.is_empty())
|
||||||
html! {
|
.then(|| html! {
|
||||||
<Icon icon={icon} />
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
html!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
{
|
|
||||||
if props.children.is_empty() {
|
|
||||||
html! ()
|
|
||||||
} else {
|
|
||||||
html! {
|
|
||||||
<span class="bp3-button-text">
|
<span class="bp3-button-text">
|
||||||
{for props.children.iter()}
|
{for children.iter()}
|
||||||
</span>
|
</span>
|
||||||
}
|
})
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</button>
|
</button>
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,16 +15,25 @@ pub struct CalloutProps {
|
||||||
pub intent: Option<Intent>,
|
pub intent: Option<Intent>,
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
pub title: Option<AttrValue>,
|
pub title: Option<AttrValue>,
|
||||||
pub children: html::Children,
|
pub children: Children,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[function_component(Callout)]
|
#[function_component(Callout)]
|
||||||
pub fn callout(props: &CalloutProps) -> Html {
|
pub fn callout(
|
||||||
let icon = if props.without_icon {
|
CalloutProps {
|
||||||
|
class,
|
||||||
|
without_icon,
|
||||||
|
icon,
|
||||||
|
intent,
|
||||||
|
title,
|
||||||
|
children,
|
||||||
|
}: &CalloutProps,
|
||||||
|
) -> Html {
|
||||||
|
let icon = if *without_icon {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
props.icon.or_else(|| {
|
icon.or_else(|| {
|
||||||
props.intent.map(|intent| match intent {
|
intent.map(|intent| match intent {
|
||||||
Intent::Primary => IconName::InfoSign,
|
Intent::Primary => IconName::InfoSign,
|
||||||
Intent::Success => IconName::Tick,
|
Intent::Success => IconName::Tick,
|
||||||
Intent::Warning => IconName::WarningSign,
|
Intent::Warning => IconName::WarningSign,
|
||||||
|
@ -32,25 +41,27 @@ pub fn callout(props: &CalloutProps) -> Html {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
let classes = classes!(
|
|
||||||
props.class.clone(),
|
|
||||||
"bp3-callout",
|
|
||||||
icon.map(|_| "bp3-callout-icon"),
|
|
||||||
props.intent,
|
|
||||||
);
|
|
||||||
html! {
|
html! {
|
||||||
<div class={classes}>
|
<div
|
||||||
|
class={classes!(
|
||||||
|
"bp3-callout",
|
||||||
|
icon.map(|_| "bp3-callout-icon"),
|
||||||
|
intent,
|
||||||
|
class.clone(),
|
||||||
|
)}
|
||||||
|
>
|
||||||
{
|
{
|
||||||
icon.iter()
|
icon.iter()
|
||||||
.map(|name| html!{<Icon icon={*name} icon_size={ICON_SIZE_LARGE}/>})
|
.map(|icon| html!{<Icon {icon} icon_size={ICON_SIZE_LARGE}/>})
|
||||||
.collect::<Html>()
|
.collect::<Html>()
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
props.title.iter()
|
title.iter()
|
||||||
.map(|title| html!{<h4 class={"bp3-heading"}>{title}</h4>})
|
.map(|title| html!{<h4 class={"bp3-heading"}>{title}</h4>})
|
||||||
.collect::<Html>()
|
.collect::<Html>()
|
||||||
}
|
}
|
||||||
{ props.children.clone() }
|
{children.clone()}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
34
src/card.rs
34
src/card.rs
|
@ -11,20 +11,34 @@ pub struct CardProps {
|
||||||
pub onclick: Callback<MouseEvent>,
|
pub onclick: Callback<MouseEvent>,
|
||||||
#[prop_or(false)]
|
#[prop_or(false)]
|
||||||
pub interactive: bool,
|
pub interactive: bool,
|
||||||
pub children: html::Children,
|
#[prop_or_default]
|
||||||
|
pub style: AttrValue,
|
||||||
|
pub children: Children,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[function_component(Card)]
|
#[function_component(Card)]
|
||||||
pub fn card(props: &CardProps) -> Html {
|
pub fn card(
|
||||||
|
CardProps {
|
||||||
|
class,
|
||||||
|
elevation,
|
||||||
|
onclick,
|
||||||
|
interactive,
|
||||||
|
style,
|
||||||
|
children,
|
||||||
|
}: &CardProps,
|
||||||
|
) -> Html {
|
||||||
html! {
|
html! {
|
||||||
<div class={classes!(
|
<div
|
||||||
"bp3-card",
|
class={classes!(
|
||||||
props.class.clone(),
|
"bp3-card",
|
||||||
props.elevation,
|
elevation,
|
||||||
props.interactive.then_some("bp3-interactive"),
|
interactive.then_some("bp3-interactive"),
|
||||||
)}
|
class.clone(),
|
||||||
onclick={props.onclick.clone()}>
|
)}
|
||||||
{props.children.clone()}
|
{onclick}
|
||||||
|
{style}
|
||||||
|
>
|
||||||
|
{children.clone()}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,33 +13,41 @@ pub struct CheckboxProps {
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
pub onchange: Callback<Event>,
|
pub onchange: Callback<Event>,
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
pub label: yew::virtual_dom::VNode,
|
pub label: Html,
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
pub indeterminate_state: bool,
|
pub class: Classes,
|
||||||
|
#[prop_or_default]
|
||||||
|
pub children: Children,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[function_component(Checkbox)]
|
#[function_component(Checkbox)]
|
||||||
pub fn checkbox(props: &CheckboxProps) -> Html {
|
pub fn checkbox(
|
||||||
|
CheckboxProps {
|
||||||
|
disabled,
|
||||||
|
inline,
|
||||||
|
large,
|
||||||
|
checked,
|
||||||
|
onchange,
|
||||||
|
label,
|
||||||
|
class,
|
||||||
|
children,
|
||||||
|
}: &CheckboxProps,
|
||||||
|
) -> Html {
|
||||||
html! {
|
html! {
|
||||||
<label
|
<label
|
||||||
class={classes!(
|
class={classes!(
|
||||||
"bp3-control", "bp3-checkbox",
|
"bp3-control",
|
||||||
props.disabled.then_some("bp3-disabled"),
|
"bp3-checkbox",
|
||||||
props.inline.then_some("bp3-inline"),
|
disabled.then_some("bp3-disabled"),
|
||||||
props.large.then_some("bp3-large")
|
inline.then_some("bp3-inline"),
|
||||||
|
large.then_some("bp3-large"),
|
||||||
|
class.clone(),
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<input
|
<input type="checkbox" checked={*checked} {onchange} disabled={*disabled} />
|
||||||
type="checkbox"
|
<span class="bp3-control-indicator" />
|
||||||
checked={props.checked}
|
{label.clone()}
|
||||||
onchange={props.onchange.clone()}
|
{children.clone()}
|
||||||
disabled={props.disabled}
|
|
||||||
/>
|
|
||||||
<span
|
|
||||||
class="bp3-control-indicator"
|
|
||||||
>
|
|
||||||
</span>
|
|
||||||
{props.label.clone()}
|
|
||||||
</label>
|
</label>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ pub struct CollapseProps {
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
pub is_open: bool,
|
pub is_open: bool,
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
pub children: html::Children,
|
pub children: Children,
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
pub keep_children_mounted: bool,
|
pub keep_children_mounted: bool,
|
||||||
#[prop_or(Duration::from_millis(200))]
|
#[prop_or(Duration::from_millis(200))]
|
||||||
|
@ -70,7 +70,7 @@ impl Component for Collapse {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn changed(&mut self, ctx: &Context<Self>) -> bool {
|
fn changed(&mut self, ctx: &Context<Self>, _old_props: &Self::Properties) -> bool {
|
||||||
if ctx.props().is_open {
|
if ctx.props().is_open {
|
||||||
match self.animation_state {
|
match self.animation_state {
|
||||||
AnimationState::Open | AnimationState::Opening => {}
|
AnimationState::Open | AnimationState::Opening => {}
|
||||||
|
|
|
@ -9,23 +9,32 @@ pub struct ControlGroupProps {
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
pub large: bool,
|
pub large: bool,
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
pub children: html::Children,
|
pub children: Children,
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
pub class: Classes,
|
pub class: Classes,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[function_component(ControlGroup)]
|
#[function_component(ControlGroup)]
|
||||||
pub fn control_group(props: &ControlGroupProps) -> Html {
|
pub fn control_group(
|
||||||
|
ControlGroupProps {
|
||||||
|
fill,
|
||||||
|
vertical,
|
||||||
|
large,
|
||||||
|
class,
|
||||||
|
children,
|
||||||
|
}: &ControlGroupProps,
|
||||||
|
) -> Html {
|
||||||
html! {
|
html! {
|
||||||
<div
|
<div
|
||||||
class={classes!(
|
class={classes!(
|
||||||
"bp3-control-group",
|
"bp3-control-group",
|
||||||
props.fill.then_some("bp3-fill"),
|
fill.then_some("bp3-fill"),
|
||||||
props.vertical.then_some("bp3-vertical"),
|
vertical.then_some("bp3-vertical"),
|
||||||
props.class.clone(),
|
large.then_some("bp3-large"),
|
||||||
|
class.clone(),
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{props.children.clone()}
|
{children.clone()}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,19 +5,17 @@ pub struct DividerProps {
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
pub vertical: bool,
|
pub vertical: bool,
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
pub children: html::Children,
|
|
||||||
#[prop_or_default]
|
|
||||||
pub class: Classes,
|
pub class: Classes,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[function_component(Divider)]
|
#[function_component(Divider)]
|
||||||
pub fn view(props: &DividerProps) -> Html {
|
pub fn view(DividerProps { vertical, class }: &DividerProps) -> Html {
|
||||||
html! {
|
html! {
|
||||||
<span
|
<span
|
||||||
class={classes!(
|
class={classes!(
|
||||||
"bp3-divider",
|
"bp3-divider",
|
||||||
props.vertical.then_some("bp3-vertical"),
|
vertical.then_some("bp3-vertical"),
|
||||||
props.class.clone(),
|
class.clone(),
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,16 +5,16 @@ pub struct ChildrenOnlyProps {
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
pub class: Classes,
|
pub class: Classes,
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
pub children: html::Children,
|
pub children: Children,
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! build_component {
|
macro_rules! build_component {
|
||||||
($name:ident, $tag:tt, $class:literal) => {
|
($name:ident, $tag:tt, $class:literal) => {
|
||||||
#[function_component($name)]
|
#[function_component($name)]
|
||||||
pub fn $tag(props: &ChildrenOnlyProps) -> Html {
|
pub fn $tag(ChildrenOnlyProps { class, children }: &ChildrenOnlyProps) -> Html {
|
||||||
html! {
|
html! {
|
||||||
<$tag class={classes!($class, props.class.clone())}>
|
<$tag class={classes!($class, class.clone())}>
|
||||||
{ props.children.clone() }
|
{children.clone()}
|
||||||
</$tag>
|
</$tag>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use implicit_clone::{unsync::IArray, ImplicitClone};
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
use crate::{Icon, IconName};
|
use crate::{Icon, IconName};
|
||||||
|
@ -11,7 +12,7 @@ pub struct HtmlSelect<T: Clone + PartialEq + 'static> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Properties)]
|
#[derive(Debug, Clone, PartialEq, Properties)]
|
||||||
pub struct HtmlSelectProps<T: Clone + PartialEq + 'static> {
|
pub struct HtmlSelectProps<T: ImplicitClone + PartialEq + 'static> {
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
pub fill: bool,
|
pub fill: bool,
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
|
@ -21,19 +22,17 @@ pub struct HtmlSelectProps<T: Clone + PartialEq + 'static> {
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
pub disabled: bool,
|
pub disabled: bool,
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
pub icon: Option<IconName>,
|
pub title: Option<AttrValue>,
|
||||||
#[prop_or_default]
|
|
||||||
pub title: Option<String>,
|
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
pub onchange: Callback<T>,
|
pub onchange: Callback<T>,
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
pub value: Option<T>,
|
pub value: Option<T>,
|
||||||
pub options: Vec<(T, String)>,
|
pub options: IArray<(T, AttrValue)>,
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
pub class: Classes,
|
pub class: Classes,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Clone + PartialEq + 'static> Component for HtmlSelect<T> {
|
impl<T: ImplicitClone + PartialEq + 'static> Component for HtmlSelect<T> {
|
||||||
type Message = Event;
|
type Message = Event;
|
||||||
type Properties = HtmlSelectProps<T>;
|
type Properties = HtmlSelectProps<T>;
|
||||||
|
|
||||||
|
@ -58,10 +57,10 @@ impl<T: Clone + PartialEq + 'static> Component for HtmlSelect<T> {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
fn changed(&mut self, ctx: &Context<Self>) -> bool {
|
fn changed(&mut self, ctx: &Context<Self>, _old_props: &Self::Properties) -> bool {
|
||||||
if let Some(value) = ctx.props().value.as_ref() {
|
if let Some(value) = ctx.props().value.as_ref() {
|
||||||
if let Some(select) = self.select_element.cast::<HtmlSelectElement>() {
|
if let Some(select) = self.select_element.cast::<HtmlSelectElement>() {
|
||||||
if let Some(i) = ctx.props().options.iter().position(|(x, _)| x == value) {
|
if let Some(i) = ctx.props().options.iter().position(|(x, _)| &x == value) {
|
||||||
if let Ok(i) = i.try_into() {
|
if let Ok(i) = i.try_into() {
|
||||||
if select.selected_index() != i {
|
if select.selected_index() != i {
|
||||||
select.set_selected_index(i);
|
select.set_selected_index(i);
|
||||||
|
@ -74,20 +73,25 @@ impl<T: Clone + PartialEq + 'static> Component for HtmlSelect<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn view(&self, ctx: &Context<Self>) -> Html {
|
fn view(&self, ctx: &Context<Self>) -> Html {
|
||||||
let option_children = ctx
|
let Self::Properties {
|
||||||
.props()
|
fill,
|
||||||
.options
|
minimal,
|
||||||
|
large,
|
||||||
|
disabled,
|
||||||
|
title,
|
||||||
|
onchange: _,
|
||||||
|
value,
|
||||||
|
options,
|
||||||
|
class,
|
||||||
|
} = &ctx.props();
|
||||||
|
|
||||||
|
let option_children = options
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(value, label)| {
|
.map(|(this_value, label)| {
|
||||||
let selected = ctx
|
let selected = value.as_ref().map(|x| &this_value == x).unwrap_or_default();
|
||||||
.props()
|
|
||||||
.value
|
|
||||||
.as_ref()
|
|
||||||
.map(|x| value == x)
|
|
||||||
.unwrap_or_default();
|
|
||||||
|
|
||||||
html! {
|
html! {
|
||||||
<option selected={selected}>
|
<option {selected}>
|
||||||
{label}
|
{label}
|
||||||
</option>
|
</option>
|
||||||
}
|
}
|
||||||
|
@ -98,18 +102,18 @@ impl<T: Clone + PartialEq + 'static> Component for HtmlSelect<T> {
|
||||||
<div
|
<div
|
||||||
class={classes!(
|
class={classes!(
|
||||||
"bp3-html-select",
|
"bp3-html-select",
|
||||||
ctx.props().minimal.then_some("bp3-minimal"),
|
minimal.then_some("bp3-minimal"),
|
||||||
ctx.props().large.then_some("bp3-large"),
|
large.then_some("bp3-large"),
|
||||||
ctx.props().fill.then_some("bp3-fill"),
|
fill.then_some("bp3-fill"),
|
||||||
ctx.props().disabled.then_some("bp3-disabled"),
|
disabled.then_some("bp3-disabled"),
|
||||||
ctx.props().class.clone(),
|
class.clone(),
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<select
|
<select
|
||||||
disabled={ctx.props().disabled}
|
value={String::new()}
|
||||||
|
disabled={*disabled}
|
||||||
onchange={ctx.link().callback(|x| x)}
|
onchange={ctx.link().callback(|x| x)}
|
||||||
title={ctx.props().title.clone()}
|
{title}
|
||||||
value={"".to_string()}
|
|
||||||
ref={self.select_element.clone()}
|
ref={self.select_element.clone()}
|
||||||
>
|
>
|
||||||
{option_children}
|
{option_children}
|
||||||
|
|
50
src/icon.rs
50
src/icon.rs
|
@ -1,4 +1,5 @@
|
||||||
use crate::Intent;
|
use crate::Intent;
|
||||||
|
use implicit_clone::ImplicitClone;
|
||||||
use yew::prelude::*;
|
use yew::prelude::*;
|
||||||
|
|
||||||
include!(concat!(env!("OUT_DIR"), "/icon_svg_paths.rs"));
|
include!(concat!(env!("OUT_DIR"), "/icon_svg_paths.rs"));
|
||||||
|
@ -12,15 +13,17 @@ impl Default for IconName {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ImplicitClone for IconName {}
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Properties)]
|
#[derive(Clone, PartialEq, Properties)]
|
||||||
pub struct IconProps {
|
pub struct IconProps {
|
||||||
pub icon: IconName,
|
pub icon: IconName,
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
pub class: Classes,
|
pub class: Classes,
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
pub title: Option<String>,
|
pub title: Option<AttrValue>,
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
pub color: Option<String>,
|
pub color: Option<AttrValue>,
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
pub intent: Option<Intent>,
|
pub intent: Option<Intent>,
|
||||||
#[prop_or(16)]
|
#[prop_or(16)]
|
||||||
|
@ -30,34 +33,47 @@ pub struct IconProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[function_component(Icon)]
|
#[function_component(Icon)]
|
||||||
pub fn icon(props: &IconProps) -> Html {
|
pub fn icon(
|
||||||
let paths = if props.icon_size == ICON_SIZE_STANDARD {
|
IconProps {
|
||||||
icon_svg_paths_16(props.icon)
|
icon,
|
||||||
|
class,
|
||||||
|
title,
|
||||||
|
color: fill,
|
||||||
|
intent,
|
||||||
|
icon_size,
|
||||||
|
onclick,
|
||||||
|
}: &IconProps,
|
||||||
|
) -> Html {
|
||||||
|
let paths = if *icon_size == ICON_SIZE_STANDARD {
|
||||||
|
icon_svg_paths_16(*icon)
|
||||||
} else {
|
} else {
|
||||||
icon_svg_paths_20(props.icon)
|
icon_svg_paths_20(*icon)
|
||||||
};
|
};
|
||||||
let pixel_grid_size = if props.icon_size >= ICON_SIZE_LARGE {
|
let pixel_grid_size = if *icon_size >= ICON_SIZE_LARGE {
|
||||||
ICON_SIZE_LARGE
|
ICON_SIZE_LARGE
|
||||||
} else {
|
} else {
|
||||||
ICON_SIZE_STANDARD
|
ICON_SIZE_STANDARD
|
||||||
};
|
};
|
||||||
let icon_string = format!("{:?}", props.icon);
|
let icon_string = AttrValue::from(format!("{:?}", icon));
|
||||||
|
let width = AttrValue::from(format!("{icon_size}"));
|
||||||
|
let height = width.clone();
|
||||||
|
|
||||||
html! {
|
html! {
|
||||||
<span
|
<span
|
||||||
class={classes!("bp3-icon", props.class.clone(), props.intent)}
|
class={classes!("bp3-icon", class.clone(), intent)}
|
||||||
onclick={props.onclick.clone()}
|
{onclick}
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
fill={props.color.clone()}
|
{fill}
|
||||||
data-icon={icon_string.clone()}
|
data-icon={&icon_string}
|
||||||
width={props.icon_size.to_string()}
|
{width}
|
||||||
height={props.icon_size.to_string()}
|
{height}
|
||||||
viewBox={format!("0 0 {x} {x}", x=pixel_grid_size)}
|
viewBox={format!("0 0 {pixel_grid_size} {pixel_grid_size}")}
|
||||||
>
|
>
|
||||||
<desc>{props.title.clone().unwrap_or(icon_string)}</desc>
|
<desc>{title.clone().unwrap_or(icon_string)}</desc>
|
||||||
{
|
{
|
||||||
paths.iter()
|
paths
|
||||||
|
.iter()
|
||||||
.map(|x| html! {
|
.map(|x| html! {
|
||||||
<path d={*x} fillRule="evenodd" />
|
<path d={*x} fillRule="evenodd" />
|
||||||
})
|
})
|
||||||
|
|
|
@ -63,13 +63,13 @@ pub struct InputGroupProps {
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
pub round: bool,
|
pub round: bool,
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
pub placeholder: String,
|
pub placeholder: AttrValue,
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
pub left_icon: Option<IconName>,
|
pub left_icon: Option<IconName>,
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
pub left_element: Option<yew::virtual_dom::VNode>,
|
pub left_element: Option<Html>,
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
pub right_element: Option<yew::virtual_dom::VNode>,
|
pub right_element: Option<Html>,
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
pub input_type: TextInputType,
|
pub input_type: TextInputType,
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
|
@ -79,7 +79,7 @@ pub struct InputGroupProps {
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
pub onkeydown: Callback<KeyboardEvent>,
|
pub onkeydown: Callback<KeyboardEvent>,
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
pub value: String,
|
pub value: AttrValue,
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
pub class: Classes,
|
pub class: Classes,
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
|
@ -104,7 +104,26 @@ impl Component for InputGroup {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn view(&self, ctx: &Context<Self>) -> Html {
|
fn view(&self, ctx: &Context<Self>) -> Html {
|
||||||
let input_style = match (self.left_element_width, self.right_element_width) {
|
let Self::Properties {
|
||||||
|
disabled,
|
||||||
|
fill,
|
||||||
|
large,
|
||||||
|
small,
|
||||||
|
round,
|
||||||
|
placeholder,
|
||||||
|
left_icon,
|
||||||
|
left_element,
|
||||||
|
right_element,
|
||||||
|
input_type,
|
||||||
|
oninput,
|
||||||
|
onkeyup,
|
||||||
|
onkeydown,
|
||||||
|
value,
|
||||||
|
class,
|
||||||
|
input_ref,
|
||||||
|
} = &ctx.props();
|
||||||
|
|
||||||
|
let style = match (self.left_element_width, self.right_element_width) {
|
||||||
(Some(left), None) => format!("padding-left:{}px", left.max(MIN_HORIZONTAL_PADDING)),
|
(Some(left), None) => format!("padding-left:{}px", left.max(MIN_HORIZONTAL_PADDING)),
|
||||||
(None, Some(right)) => format!("padding-right:{}px", right.max(MIN_HORIZONTAL_PADDING)),
|
(None, Some(right)) => format!("padding-right:{}px", right.max(MIN_HORIZONTAL_PADDING)),
|
||||||
(Some(left), Some(right)) => format!(
|
(Some(left), Some(right)) => format!(
|
||||||
|
@ -119,16 +138,16 @@ impl Component for InputGroup {
|
||||||
<div
|
<div
|
||||||
class={classes!(
|
class={classes!(
|
||||||
"bp3-input-group",
|
"bp3-input-group",
|
||||||
ctx.props().disabled.then_some("bp3-disabled"),
|
disabled.then_some("bp3-disabled"),
|
||||||
ctx.props().fill.then_some("bp3-fill"),
|
fill.then_some("bp3-fill"),
|
||||||
ctx.props().large.then_some("bp3-large"),
|
large.then_some("bp3-large"),
|
||||||
ctx.props().small.then_some("bp3-small"),
|
small.then_some("bp3-small"),
|
||||||
ctx.props().round.then_some("bp3-round"),
|
round.then_some("bp3-round"),
|
||||||
ctx.props().class.clone(),
|
class.clone(),
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{
|
{
|
||||||
if let Some(left_element) = ctx.props().left_element.clone() {
|
if let Some(left_element) = left_element.clone() {
|
||||||
html! {
|
html! {
|
||||||
<span
|
<span
|
||||||
class="bp3-input-left-container"
|
class="bp3-input-left-container"
|
||||||
|
@ -137,7 +156,7 @@ impl Component for InputGroup {
|
||||||
{left_element}
|
{left_element}
|
||||||
</span>
|
</span>
|
||||||
}
|
}
|
||||||
} else if let Some(icon) = ctx.props().left_icon {
|
} else if let Some(icon) = left_icon {
|
||||||
html! {
|
html! {
|
||||||
<Icon icon={icon} />
|
<Icon icon={icon} />
|
||||||
}
|
}
|
||||||
|
@ -146,19 +165,19 @@ impl Component for InputGroup {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
<input
|
<input
|
||||||
ref={ctx.props().input_ref.clone()}
|
ref={input_ref.clone()}
|
||||||
class="bp3-input"
|
class="bp3-input"
|
||||||
type={ctx.props().input_type.as_str()}
|
type={input_type.as_str()}
|
||||||
placeholder={ctx.props().placeholder.clone()}
|
placeholder={placeholder.clone()}
|
||||||
disabled={ctx.props().disabled}
|
disabled={*disabled}
|
||||||
oninput={ctx.props().oninput.clone()}
|
{oninput}
|
||||||
onkeyup={ctx.props().onkeyup.clone()}
|
{onkeyup}
|
||||||
onkeydown={ctx.props().onkeydown.clone()}
|
{onkeydown}
|
||||||
value={ctx.props().value.clone()}
|
{value}
|
||||||
style={input_style}
|
{style}
|
||||||
/>
|
/>
|
||||||
{
|
{
|
||||||
if let Some(right_element) = ctx.props().right_element.clone() {
|
if let Some(right_element) = right_element.clone() {
|
||||||
html! {
|
html! {
|
||||||
<span
|
<span
|
||||||
class="bp3-input-action"
|
class="bp3-input-action"
|
||||||
|
|
15
src/lib.rs
15
src/lib.rs
|
@ -64,6 +64,7 @@ pub use text_area::*;
|
||||||
#[cfg(feature = "tree")]
|
#[cfg(feature = "tree")]
|
||||||
pub use tree::*;
|
pub use tree::*;
|
||||||
|
|
||||||
|
use implicit_clone::ImplicitClone;
|
||||||
use yew::Classes;
|
use yew::Classes;
|
||||||
|
|
||||||
// See https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/buttons
|
// See https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/buttons
|
||||||
|
@ -87,6 +88,8 @@ pub enum Intent {
|
||||||
Danger,
|
Danger,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ImplicitClone for Intent {}
|
||||||
|
|
||||||
impl From<Intent> for Classes {
|
impl From<Intent> for Classes {
|
||||||
fn from(intent: Intent) -> Self {
|
fn from(intent: Intent) -> Self {
|
||||||
use Intent::*;
|
use Intent::*;
|
||||||
|
@ -146,14 +149,16 @@ impl Elevation {
|
||||||
|
|
||||||
impl Default for Elevation {
|
impl Default for Elevation {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Elevation::Level0
|
Self::Level0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ImplicitClone for Elevation {}
|
||||||
|
|
||||||
impl From<Elevation> for Classes {
|
impl From<Elevation> for Classes {
|
||||||
fn from(elevation: Elevation) -> Self {
|
fn from(elevation: Elevation) -> Self {
|
||||||
use Elevation::*;
|
use Elevation::*;
|
||||||
Classes::from(match elevation {
|
Self::from(match elevation {
|
||||||
Level0 => "bp3-elevation-0",
|
Level0 => "bp3-elevation-0",
|
||||||
Level1 => "bp3-elevation-1",
|
Level1 => "bp3-elevation-1",
|
||||||
Level2 => "bp3-elevation-2",
|
Level2 => "bp3-elevation-2",
|
||||||
|
@ -162,3 +167,9 @@ impl From<Elevation> for Classes {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<&Elevation> for Classes {
|
||||||
|
fn from(elevation: &Elevation) -> Self {
|
||||||
|
Self::from(*elevation)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
55
src/menu.rs
55
src/menu.rs
|
@ -10,7 +10,7 @@ pub struct MenuProps {
|
||||||
pub class: Classes,
|
pub class: Classes,
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
pub r#ref: NodeRef,
|
pub r#ref: NodeRef,
|
||||||
pub children: html::Children,
|
pub children: Children,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[function_component(Menu)]
|
#[function_component(Menu)]
|
||||||
|
@ -32,7 +32,7 @@ pub fn menu(props: &MenuProps) -> Html {
|
||||||
#[derive(Clone, PartialEq, Properties)]
|
#[derive(Clone, PartialEq, Properties)]
|
||||||
pub struct MenuItemProps {
|
pub struct MenuItemProps {
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
pub text: yew::virtual_dom::VNode,
|
pub text: Html,
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
pub text_class: Classes,
|
pub text_class: Classes,
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
|
@ -44,7 +44,7 @@ pub struct MenuItemProps {
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
pub href: Option<AttrValue>,
|
pub href: Option<AttrValue>,
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
pub label: Option<yew::virtual_dom::VNode>,
|
pub label: Option<Html>,
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
pub label_class: Classes,
|
pub label_class: Classes,
|
||||||
// TODO: pub multiline: bool, (requires <Text>)
|
// TODO: pub multiline: bool, (requires <Text>)
|
||||||
|
@ -57,32 +57,47 @@ pub struct MenuItemProps {
|
||||||
pub icon_html: Option<Html>,
|
pub icon_html: Option<Html>,
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
pub onclick: Callback<MouseEvent>,
|
pub onclick: Callback<MouseEvent>,
|
||||||
// TODO: pub children: html::Children,
|
// TODO: pub children: Children,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[function_component(MenuItem)]
|
#[function_component(MenuItem)]
|
||||||
pub fn menu_item(props: &MenuItemProps) -> Html {
|
pub fn menu_item(
|
||||||
|
MenuItemProps {
|
||||||
|
text,
|
||||||
|
text_class,
|
||||||
|
active,
|
||||||
|
class,
|
||||||
|
disabled,
|
||||||
|
href,
|
||||||
|
label,
|
||||||
|
label_class,
|
||||||
|
intent,
|
||||||
|
icon,
|
||||||
|
icon_html,
|
||||||
|
onclick,
|
||||||
|
}: &MenuItemProps,
|
||||||
|
) -> Html {
|
||||||
html! {
|
html! {
|
||||||
<li>
|
<li>
|
||||||
<a
|
<a
|
||||||
class={classes!(
|
class={classes!(
|
||||||
"bp3-menu-item",
|
"bp3-menu-item",
|
||||||
props.active.then_some("bp3-active"),
|
active.then_some("bp3-active"),
|
||||||
props.disabled.then_some("bp3-disabled"),
|
disabled.then_some("bp3-disabled"),
|
||||||
props.intent
|
intent
|
||||||
.or_else(|| props.active.then_some(Intent::Primary)),
|
.or_else(|| active.then_some(Intent::Primary)),
|
||||||
props.class.clone(),
|
class.clone(),
|
||||||
)}
|
)}
|
||||||
href={(!props.disabled).then(|| props.href.clone()).flatten()}
|
href={(!disabled).then(|| href.clone()).flatten()}
|
||||||
tabIndex={(!props.disabled).then_some("0")}
|
tabIndex={(!disabled).then_some("0")}
|
||||||
onclick={(!props.disabled).then(|| props.onclick.clone())}
|
onclick={(!disabled).then(|| onclick.clone())}
|
||||||
>
|
>
|
||||||
{
|
{
|
||||||
if let Some(icon_name) = props.icon {
|
if let Some(icon_name) = icon {
|
||||||
html! {
|
html! {
|
||||||
<Icon icon={icon_name} />
|
<Icon icon={icon_name} />
|
||||||
}
|
}
|
||||||
} else if let Some(html) = props.icon_html.clone() {
|
} else if let Some(html) = icon_html.clone() {
|
||||||
html
|
html
|
||||||
} else {
|
} else {
|
||||||
html! {
|
html! {
|
||||||
|
@ -90,16 +105,16 @@ pub fn menu_item(props: &MenuItemProps) -> Html {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
<div class={classes!("bp3-text", "bp3-fill", props.text_class.clone())}>
|
<div class={classes!("bp3-text", "bp3-fill", text_class.clone())}>
|
||||||
{props.text.clone()}
|
{text.clone()}
|
||||||
</div>
|
</div>
|
||||||
{
|
{
|
||||||
if let Some(label) = props.label.clone() {
|
if let Some(label) = label.clone() {
|
||||||
html! {
|
html! {
|
||||||
<span
|
<span
|
||||||
class={classes!(
|
class={classes!(
|
||||||
"bp3-menu-item-label",
|
"bp3-menu-item-label",
|
||||||
props.label_class.clone())}
|
label_class.clone())}
|
||||||
>
|
>
|
||||||
{label}
|
{label}
|
||||||
</span>
|
</span>
|
||||||
|
@ -117,7 +132,7 @@ pub fn menu_item(props: &MenuItemProps) -> Html {
|
||||||
#[derive(Clone, PartialEq, Properties)]
|
#[derive(Clone, PartialEq, Properties)]
|
||||||
pub struct MenuDividerProps {
|
pub struct MenuDividerProps {
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
pub title: Option<yew::virtual_dom::VNode>,
|
pub title: Option<Html>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[function_component(MenuDivider)]
|
#[function_component(MenuDivider)]
|
||||||
|
|
|
@ -42,9 +42,9 @@ where
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
pub large: bool,
|
pub large: bool,
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
pub class: String,
|
pub class: Classes,
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
pub placeholder: String,
|
pub placeholder: AttrValue,
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
pub left_icon: Option<IconName>,
|
pub left_icon: Option<IconName>,
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
|
@ -120,7 +120,7 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn changed(&mut self, ctx: &Context<Self>) -> bool {
|
fn changed(&mut self, ctx: &Context<Self>, _old_props: &Self::Properties) -> bool {
|
||||||
self.input = ctx.props().value.to_string();
|
self.input = ctx.props().value.to_string();
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
@ -132,14 +132,24 @@ where
|
||||||
disabled,
|
disabled,
|
||||||
disable_buttons,
|
disable_buttons,
|
||||||
buttons_on_the_left,
|
buttons_on_the_left,
|
||||||
..
|
large,
|
||||||
} = *ctx.props();
|
placeholder,
|
||||||
|
left_icon,
|
||||||
|
left_element,
|
||||||
|
right_element,
|
||||||
|
fill,
|
||||||
|
bounds,
|
||||||
|
class,
|
||||||
|
intent,
|
||||||
|
onchange: _,
|
||||||
|
} = &ctx.props();
|
||||||
|
|
||||||
let bounds = &ctx.props().bounds;
|
let button_up_disabled =
|
||||||
let button_up_disabled = disabled || bounds.clamp(value + increment, increment) == value;
|
*disabled || bounds.clamp(*value + *increment, *increment) == *value;
|
||||||
let button_down_disabled = disabled || bounds.clamp(value - increment, increment) == value;
|
let button_down_disabled =
|
||||||
|
*disabled || bounds.clamp(*value - *increment, *increment) == *value;
|
||||||
|
|
||||||
let buttons = if disable_buttons {
|
let buttons = if *disable_buttons {
|
||||||
html!()
|
html!()
|
||||||
} else {
|
} else {
|
||||||
html! {
|
html! {
|
||||||
|
@ -148,11 +158,13 @@ where
|
||||||
icon={IconName::ChevronUp}
|
icon={IconName::ChevronUp}
|
||||||
disabled={button_up_disabled}
|
disabled={button_up_disabled}
|
||||||
onclick={ctx.link().callback(|_| Msg::Up)}
|
onclick={ctx.link().callback(|_| Msg::Up)}
|
||||||
|
{intent}
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
icon={IconName::ChevronDown}
|
icon={IconName::ChevronDown}
|
||||||
disabled={button_down_disabled}
|
disabled={button_down_disabled}
|
||||||
onclick={ctx.link().callback(|_| Msg::Down)}
|
onclick={ctx.link().callback(|_| Msg::Down)}
|
||||||
|
{intent}
|
||||||
/>
|
/>
|
||||||
</ButtonGroup>
|
</ButtonGroup>
|
||||||
}
|
}
|
||||||
|
@ -160,12 +172,12 @@ where
|
||||||
|
|
||||||
let input_group = html! {
|
let input_group = html! {
|
||||||
<InputGroup
|
<InputGroup
|
||||||
placeholder={ctx.props().placeholder.clone()}
|
{placeholder}
|
||||||
large={ctx.props().large}
|
{large}
|
||||||
disabled={ctx.props().disabled}
|
{disabled}
|
||||||
left_icon={ctx.props().left_icon}
|
{left_icon}
|
||||||
left_element={ctx.props().left_element.clone()}
|
left_element={left_element.clone()}
|
||||||
right_element={ctx.props().right_element.clone()}
|
right_element={right_element.clone()}
|
||||||
value={self.input.clone()}
|
value={self.input.clone()}
|
||||||
oninput={ctx.link().callback(|e: InputEvent| {
|
oninput={ctx.link().callback(|e: InputEvent| {
|
||||||
let value = e.target_unchecked_into::<HtmlInputElement>().value();
|
let value = e.target_unchecked_into::<HtmlInputElement>().value();
|
||||||
|
@ -183,12 +195,12 @@ where
|
||||||
/>
|
/>
|
||||||
};
|
};
|
||||||
|
|
||||||
if buttons_on_the_left {
|
if *buttons_on_the_left {
|
||||||
html! {
|
html! {
|
||||||
<ControlGroup
|
<ControlGroup
|
||||||
class={classes!("bp3-numeric-input")}
|
class={classes!("bp3-numeric-input", class.clone())}
|
||||||
fill={ctx.props().fill}
|
{fill}
|
||||||
large={ctx.props().large}
|
{large}
|
||||||
>
|
>
|
||||||
{buttons}
|
{buttons}
|
||||||
{input_group}
|
{input_group}
|
||||||
|
@ -197,9 +209,9 @@ where
|
||||||
} else {
|
} else {
|
||||||
html! {
|
html! {
|
||||||
<ControlGroup
|
<ControlGroup
|
||||||
class={classes!("bp3-numeric-input")}
|
class={classes!("bp3-numeric-input", class.clone())}
|
||||||
fill={ctx.props().fill}
|
{fill}
|
||||||
large={ctx.props().large}
|
{large}
|
||||||
>
|
>
|
||||||
{input_group}
|
{input_group}
|
||||||
{buttons}
|
{buttons}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use crate::{Button, IconName};
|
use crate::{Button, IconName};
|
||||||
use gloo::timers::callback::Timeout;
|
use gloo::timers::callback::Timeout;
|
||||||
|
use implicit_clone::ImplicitClone;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
@ -42,6 +43,8 @@ pub struct PanelStackState {
|
||||||
action: Option<StateAction>,
|
action: Option<StateAction>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ImplicitClone for PanelStackState {}
|
||||||
|
|
||||||
impl PanelStackState {
|
impl PanelStackState {
|
||||||
pub fn new(content: Html) -> PanelBuilder<fn(Option<Html>, Html) -> Self, Html, Self> {
|
pub fn new(content: Html) -> PanelBuilder<fn(Option<Html>, Html) -> Self, Html, Self> {
|
||||||
PanelBuilder::new(content, |title, content| {
|
PanelBuilder::new(content, |title, content| {
|
||||||
|
@ -204,7 +207,7 @@ impl Component for PanelStack {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn changed(&mut self, ctx: &Context<Self>) -> bool {
|
fn changed(&mut self, ctx: &Context<Self>, _old_props: &Self::Properties) -> bool {
|
||||||
self.action_to_perform = ctx.props().state.action;
|
self.action_to_perform = ctx.props().state.action;
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
@ -293,8 +296,8 @@ impl Component for Panel {
|
||||||
html! {
|
html! {
|
||||||
<div class={classes} style={style}>
|
<div class={classes} style={style}>
|
||||||
<div class="bp3-panel-stack-header">
|
<div class="bp3-panel-stack-header">
|
||||||
<span>{back_button.unwrap_or_default()}</span>
|
<span>{back_button}</span>
|
||||||
{ctx.props().title.clone().unwrap_or_default()}
|
{ctx.props().title.clone()}
|
||||||
<span/>
|
<span/>
|
||||||
</div>
|
</div>
|
||||||
{for ctx.props().children.iter()}
|
{for ctx.props().children.iter()}
|
||||||
|
@ -302,7 +305,7 @@ impl Component for Panel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn changed(&mut self, ctx: &Context<Self>) -> bool {
|
fn changed(&mut self, ctx: &Context<Self>, _old_props: &Self::Properties) -> bool {
|
||||||
self.animation = ctx.props().animation;
|
self.animation = ctx.props().animation;
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,26 +16,35 @@ pub struct ProgressBarProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[function_component(ProgressBar)]
|
#[function_component(ProgressBar)]
|
||||||
pub fn progress_bar(props: &ProgressBarProps) -> Html {
|
pub fn progress_bar(
|
||||||
let width = if let Some(value) = props.value {
|
ProgressBarProps {
|
||||||
|
animate,
|
||||||
|
stripes,
|
||||||
|
value,
|
||||||
|
intent,
|
||||||
|
class,
|
||||||
|
}: &ProgressBarProps,
|
||||||
|
) -> Html {
|
||||||
|
let style = if let Some(value) = value {
|
||||||
// NOTE: nightly, issue #44095 for f32::clamp
|
// NOTE: nightly, issue #44095 for f32::clamp
|
||||||
// let percent = ((1000. * value).ceil() / 10.).clamp(0.,100.);
|
// let percent = ((1000. * value).ceil() / 10.).clamp(0.,100.);
|
||||||
let percent = ((1000. * value).ceil() / 10.).max(0.).min(100.);
|
let percent = ((1000. * value).ceil() / 10.).max(0.).min(100.);
|
||||||
format!("width: {}%;", percent)
|
AttrValue::from(format!("width: {}%;", percent))
|
||||||
} else {
|
} else {
|
||||||
"".into()
|
"".into()
|
||||||
};
|
};
|
||||||
|
|
||||||
html! {
|
html! {
|
||||||
<div
|
<div
|
||||||
class={classes!(
|
class={classes!(
|
||||||
"bp3-progress-bar",
|
"bp3-progress-bar",
|
||||||
props.intent,
|
intent,
|
||||||
(!props.animate).then_some("bp3-no-animation"),
|
(!animate).then_some("bp3-no-animation"),
|
||||||
(!props.stripes).then_some("bp3-no-stripes"),
|
(!stripes).then_some("bp3-no-stripes"),
|
||||||
props.class.clone(),
|
class.clone(),
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div class={classes!("bp3-progress-meter")} style={{width}}/>
|
<div class={classes!("bp3-progress-meter")} {style}/>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
45
src/radio.rs
45
src/radio.rs
|
@ -8,43 +8,58 @@ pub struct RadioProps {
|
||||||
pub inline: bool,
|
pub inline: bool,
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
pub large: bool,
|
pub large: bool,
|
||||||
|
#[prop_or(false)]
|
||||||
|
pub checked: bool,
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
pub checked: Option<bool>,
|
pub name: Option<AttrValue>,
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
pub name: Option<String>,
|
pub onchange: Callback<Event>,
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
pub onchange: Option<Callback<Event>>,
|
pub label: Html,
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
pub label: yew::virtual_dom::VNode,
|
pub value: Option<AttrValue>,
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
pub value: Option<String>,
|
pub class: Classes,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[function_component(Radio)]
|
#[function_component(Radio)]
|
||||||
pub fn radio(props: &RadioProps) -> Html {
|
pub fn radio(
|
||||||
|
RadioProps {
|
||||||
|
disabled,
|
||||||
|
inline,
|
||||||
|
large,
|
||||||
|
checked,
|
||||||
|
name,
|
||||||
|
onchange,
|
||||||
|
label,
|
||||||
|
value,
|
||||||
|
class,
|
||||||
|
}: &RadioProps,
|
||||||
|
) -> Html {
|
||||||
html! {
|
html! {
|
||||||
<label
|
<label
|
||||||
class={classes!(
|
class={classes!(
|
||||||
"bp3-control",
|
"bp3-control",
|
||||||
"bp3-radio",
|
"bp3-radio",
|
||||||
props.disabled.then_some("bp3-disabled"),
|
disabled.then_some("bp3-disabled"),
|
||||||
props.inline.then_some("bp3-inline"),
|
inline.then_some("bp3-inline"),
|
||||||
props.large.then_some("bp3-large"),
|
large.then_some("bp3-large"),
|
||||||
|
class.clone(),
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
type="radio"
|
type="radio"
|
||||||
onchange={props.onchange.clone().unwrap_or_default()}
|
{onchange}
|
||||||
disabled={props.disabled}
|
disabled={*disabled}
|
||||||
value={props.value.clone().unwrap_or_default()}
|
{value}
|
||||||
checked={props.checked.unwrap_or(false)}
|
checked={*checked}
|
||||||
name={props.name.clone().unwrap_or_default()}
|
{name}
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
class={classes!("bp3-control-indicator")}
|
class={classes!("bp3-control-indicator")}
|
||||||
>
|
>
|
||||||
</span>
|
</span>
|
||||||
{props.label.clone()}
|
{label.clone()}
|
||||||
</label>
|
</label>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +1,18 @@
|
||||||
use crate::Radio;
|
use crate::Radio;
|
||||||
|
use implicit_clone::{unsync::IArray, ImplicitClone};
|
||||||
use yew::prelude::*;
|
use yew::prelude::*;
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Properties)]
|
#[derive(Clone, PartialEq, Properties)]
|
||||||
pub struct RadioGroupProps<T: Clone + PartialEq + 'static> {
|
pub struct RadioGroupProps<T: ImplicitClone + PartialEq + 'static> {
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
pub label: Option<yew::virtual_dom::VNode>,
|
pub label: Option<Html>,
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
pub disabled: bool,
|
pub disabled: bool,
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
pub inline: bool,
|
pub inline: bool,
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
pub large: bool,
|
pub large: bool,
|
||||||
pub options: Vec<(T, String)>,
|
pub options: IArray<(T, AttrValue)>,
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
pub value: Option<T>,
|
pub value: Option<T>,
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
|
@ -20,26 +21,33 @@ pub struct RadioGroupProps<T: Clone + PartialEq + 'static> {
|
||||||
pub class: Classes,
|
pub class: Classes,
|
||||||
}
|
}
|
||||||
|
|
||||||
// impl<T: Clone + PartialEq + 'static> Component for RadioGroup<T> {
|
|
||||||
|
|
||||||
#[function_component(RadioGroup)]
|
#[function_component(RadioGroup)]
|
||||||
pub fn radio_group<T: Clone + PartialEq + 'static>(props: &RadioGroupProps<T>) -> Html {
|
pub fn radio_group<T: ImplicitClone + PartialEq + 'static>(
|
||||||
let option_children = props
|
RadioGroupProps {
|
||||||
.options
|
label,
|
||||||
|
disabled,
|
||||||
|
inline,
|
||||||
|
large,
|
||||||
|
options,
|
||||||
|
value,
|
||||||
|
onchange,
|
||||||
|
class,
|
||||||
|
}: &RadioGroupProps<T>,
|
||||||
|
) -> Html {
|
||||||
|
let option_children = options
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(value, label)| {
|
.map(|(this_value, label)| {
|
||||||
let checked = props.value.as_ref().map(|x| value == x).unwrap_or_default();
|
let checked = value.as_ref().map(|x| &this_value == x).unwrap_or_default();
|
||||||
let value = value.clone();
|
|
||||||
|
|
||||||
html! {
|
html! {
|
||||||
<Radio
|
<Radio
|
||||||
value={"".to_string()}
|
value={String::new()}
|
||||||
label={html!(label)}
|
label={html!(label)}
|
||||||
checked={checked}
|
{checked}
|
||||||
onchange={props.onchange.reform(move |_| value.clone())}
|
onchange={onchange.reform(move |_| this_value.clone())}
|
||||||
inline={props.inline}
|
{inline}
|
||||||
disabled={props.disabled}
|
{disabled}
|
||||||
large={props.large}
|
{large}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -49,16 +57,10 @@ pub fn radio_group<T: Clone + PartialEq + 'static>(props: &RadioGroupProps<T>) -
|
||||||
<div
|
<div
|
||||||
class={classes!(
|
class={classes!(
|
||||||
"bp3-radio-group",
|
"bp3-radio-group",
|
||||||
props.class.clone(),
|
class.clone(),
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{
|
{label.clone()}
|
||||||
if let Some(label) = props.label.clone() {
|
|
||||||
label
|
|
||||||
} else {
|
|
||||||
html!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
{option_children}
|
{option_children}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
use crate::Intent;
|
use crate::Intent;
|
||||||
use std::borrow::Cow;
|
use implicit_clone::{unsync::IArray, ImplicitClone};
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use wasm_bindgen::closure::Closure;
|
use wasm_bindgen::closure::Closure;
|
||||||
use wasm_bindgen::JsCast;
|
use wasm_bindgen::JsCast;
|
||||||
use web_sys::Element;
|
use web_sys::Element;
|
||||||
use yew::prelude::*;
|
use yew::prelude::*;
|
||||||
|
|
||||||
pub struct Slider<T: Clone + PartialEq + 'static> {
|
pub struct Slider<T: ImplicitClone + PartialEq + 'static> {
|
||||||
mouse_move: Closure<dyn FnMut(MouseEvent)>,
|
mouse_move: Closure<dyn FnMut(MouseEvent)>,
|
||||||
mouse_up: Closure<dyn FnMut(MouseEvent)>,
|
mouse_up: Closure<dyn FnMut(MouseEvent)>,
|
||||||
handle_ref: NodeRef,
|
handle_ref: NodeRef,
|
||||||
|
@ -17,7 +17,7 @@ pub struct Slider<T: Clone + PartialEq + 'static> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Properties)]
|
#[derive(Clone, PartialEq, Properties)]
|
||||||
pub struct SliderProps<T: Clone + PartialEq + 'static> {
|
pub struct SliderProps<T: ImplicitClone + PartialEq + 'static> {
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
pub class: Classes,
|
pub class: Classes,
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
|
@ -25,9 +25,9 @@ pub struct SliderProps<T: Clone + PartialEq + 'static> {
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
pub intent: Option<Intent>,
|
pub intent: Option<Intent>,
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
pub value_label: Option<Cow<'static, str>>,
|
pub value_label: Option<AttrValue>,
|
||||||
pub onchange: Callback<T>,
|
pub onchange: Callback<T>,
|
||||||
pub values: Vec<(T, Option<Cow<'static, str>>)>,
|
pub values: IArray<(T, Option<AttrValue>)>,
|
||||||
pub selected: Option<T>,
|
pub selected: Option<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ pub enum Msg {
|
||||||
Keyboard(KeyboardEvent),
|
Keyboard(KeyboardEvent),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Clone + PartialEq + 'static> Component for Slider<T> {
|
impl<T: ImplicitClone + PartialEq + 'static> Component for Slider<T> {
|
||||||
type Message = Msg;
|
type Message = Msg;
|
||||||
type Properties = SliderProps<T>;
|
type Properties = SliderProps<T>;
|
||||||
|
|
||||||
|
@ -98,13 +98,16 @@ impl<T: Clone + PartialEq + 'static> Component for Slider<T> {
|
||||||
|
|
||||||
let position = (pixel_delta / tick_size).round() as usize;
|
let position = (pixel_delta / tick_size).round() as usize;
|
||||||
|
|
||||||
let (value, _) =
|
let (value, _) = ctx.props().values.get(position).unwrap_or_else(|| {
|
||||||
ctx.props().values.get(position).unwrap_or_else(|| {
|
ctx.props()
|
||||||
ctx.props().values.last().expect("No value in the vec")
|
.values
|
||||||
});
|
.last()
|
||||||
|
.cloned()
|
||||||
|
.expect("No value in the array")
|
||||||
|
});
|
||||||
|
|
||||||
if Some(value) != ctx.props().selected.as_ref() {
|
if Some(&value) != ctx.props().selected.as_ref() {
|
||||||
ctx.props().onchange.emit(value.clone());
|
ctx.props().onchange.emit(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
true
|
true
|
||||||
|
@ -139,7 +142,7 @@ impl<T: Clone + PartialEq + 'static> Component for Slider<T> {
|
||||||
.props()
|
.props()
|
||||||
.values
|
.values
|
||||||
.iter()
|
.iter()
|
||||||
.position(|(value, _)| Some(value) == ctx.props().selected.as_ref())
|
.position(|(value, _)| Some(value) == ctx.props().selected)
|
||||||
.map(|i| i.saturating_sub(1))
|
.map(|i| i.saturating_sub(1))
|
||||||
.unwrap_or(0);
|
.unwrap_or(0);
|
||||||
let (value, _) = ctx.props().values[index].clone();
|
let (value, _) = ctx.props().values[index].clone();
|
||||||
|
@ -153,20 +156,16 @@ impl<T: Clone + PartialEq + 'static> Component for Slider<T> {
|
||||||
.props()
|
.props()
|
||||||
.values
|
.values
|
||||||
.iter()
|
.iter()
|
||||||
.position(|(value, _)| Some(value) == ctx.props().selected.as_ref())
|
.position(|(value, _)| Some(value) == ctx.props().selected)
|
||||||
.map(|i| i.saturating_add(1))
|
.map(|i| i.saturating_add(1))
|
||||||
.unwrap_or(0);
|
.unwrap_or(0);
|
||||||
let (value, _) = ctx
|
let (value, _) = ctx.props().values.get(index).unwrap_or_else(|| {
|
||||||
.props()
|
let (value, label) = ctx.props().values.last().expect(
|
||||||
.values
|
"Already check, \
|
||||||
.get(index)
|
|
||||||
.unwrap_or_else(|| {
|
|
||||||
ctx.props().values.last().expect(
|
|
||||||
"Already check, \
|
|
||||||
there are at least 2 values in ctx.props().options; qed",
|
there are at least 2 values in ctx.props().options; qed",
|
||||||
)
|
);
|
||||||
})
|
(value.clone(), label.clone())
|
||||||
.clone();
|
});
|
||||||
ctx.props().onchange.emit(value);
|
ctx.props().onchange.emit(value);
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
@ -181,14 +180,14 @@ impl<T: Clone + PartialEq + 'static> Component for Slider<T> {
|
||||||
.props()
|
.props()
|
||||||
.values
|
.values
|
||||||
.iter()
|
.iter()
|
||||||
.position(|(value, _)| Some(value) == ctx.props().selected.as_ref());
|
.position(|(value, _)| Some(value) == ctx.props().selected);
|
||||||
let labels = if ctx.props().values.len() > 1 {
|
let labels = if ctx.props().values.len() > 1 {
|
||||||
ctx.props()
|
ctx.props()
|
||||||
.values
|
.values
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.filter_map(|(i, (_, label))| {
|
.filter_map(|(i, (_, label))| {
|
||||||
label.clone().map(|x| {
|
label.map(|x| {
|
||||||
html! {
|
html! {
|
||||||
<div
|
<div
|
||||||
class={classes!("bp3-slider-label")}
|
class={classes!("bp3-slider-label")}
|
||||||
|
@ -320,7 +319,7 @@ impl<T: Clone + PartialEq + 'static> Component for Slider<T> {
|
||||||
onkeydown={ctx.link().callback(|event| Msg::Keyboard(event))}
|
onkeydown={ctx.link().callback(|event| Msg::Keyboard(event))}
|
||||||
tabindex=0
|
tabindex=0
|
||||||
>
|
>
|
||||||
{value_label.clone().unwrap_or_default()}
|
{value_label.clone()}
|
||||||
</span>
|
</span>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -334,7 +333,7 @@ impl<T: Clone + PartialEq + 'static> Component for Slider<T> {
|
||||||
ref={self.handle_ref.clone()}
|
ref={self.handle_ref.clone()}
|
||||||
style="left: calc(50% - 8px);"
|
style="left: calc(50% - 8px);"
|
||||||
>
|
>
|
||||||
{value_label.clone().unwrap_or_default()}
|
{value_label.clone()}
|
||||||
</span>
|
</span>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,12 +35,13 @@ pub fn spinner(props: &SpinnerProps) -> Html {
|
||||||
view_box_x, view_box_x, view_box_width, view_box_width,
|
view_box_x, view_box_x, view_box_width, view_box_width,
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
let spinner_track = format!(
|
let spinner_track = AttrValue::from(format!(
|
||||||
"M 50,50 m 0,-{R:.0} a {R:.0},{R:.0} 0 1 1 0,{R2:.0} a {R:.0},{R:.0} 0 1 1 0,-{R2:.0}",
|
"M 50,50 m 0,-{R:.0} a {R:.0},{R:.0} 0 1 1 0,{R2:.0} a {R:.0},{R:.0} 0 1 1 0,-{R2:.0}",
|
||||||
R = R,
|
|
||||||
R2 = R * 2.0,
|
R2 = R * 2.0,
|
||||||
);
|
));
|
||||||
let stroke_offset = PATH_LENGTH - PATH_LENGTH * props.value.clamp(0.0, 1.0);
|
let stroke_offset = PATH_LENGTH - PATH_LENGTH * props.value.clamp(0.0, 1.0);
|
||||||
|
let width = AttrValue::from(format!("{size}"));
|
||||||
|
let height = width.clone();
|
||||||
|
|
||||||
html! {
|
html! {
|
||||||
<div
|
<div
|
||||||
|
@ -54,18 +55,18 @@ pub fn spinner(props: &SpinnerProps) -> Html {
|
||||||
class={classes!("bp3-spinner-animation")}
|
class={classes!("bp3-spinner-animation")}
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
width={size.to_string()}
|
{width}
|
||||||
height={size.to_string()}
|
{height}
|
||||||
stroke-width={stroke_width.to_string()}
|
stroke-width={stroke_width.to_string()}
|
||||||
viewBox={view_box}
|
viewBox={view_box}
|
||||||
>
|
>
|
||||||
<path
|
<path
|
||||||
class={classes!("bp3-spinner-track")}
|
class={classes!("bp3-spinner-track")}
|
||||||
d={spinner_track.clone()}
|
d={&spinner_track}
|
||||||
/>
|
/>
|
||||||
<path
|
<path
|
||||||
class={classes!("bp3-spinner-head")}
|
class={classes!("bp3-spinner-head")}
|
||||||
d={spinner_track}
|
d={&spinner_track}
|
||||||
pathLength={PATH_LENGTH.to_string()}
|
pathLength={PATH_LENGTH.to_string()}
|
||||||
stroke-dasharray={format!("{} {}", PATH_LENGTH, PATH_LENGTH)}
|
stroke-dasharray={format!("{} {}", PATH_LENGTH, PATH_LENGTH)}
|
||||||
stroke-dashoffset={stroke_offset.to_string()}
|
stroke-dashoffset={stroke_offset.to_string()}
|
||||||
|
|
|
@ -13,75 +13,85 @@ pub struct SwitchProps {
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
pub onclick: Callback<MouseEvent>,
|
pub onclick: Callback<MouseEvent>,
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
pub label: yew::virtual_dom::VNode,
|
pub label: Html,
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
pub class: Classes,
|
pub class: Classes,
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
pub inner_label_checked: Option<String>,
|
pub inner_label_checked: Option<AttrValue>,
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
pub inner_label: Option<String>,
|
pub inner_label: Option<AttrValue>,
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
pub align_right: bool,
|
pub align_right: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[function_component(Switch)]
|
#[function_component(Switch)]
|
||||||
pub fn switch(props: &SwitchProps) -> Html {
|
pub fn switch(
|
||||||
let display_label = {
|
SwitchProps {
|
||||||
if props.inner_label.is_some() || props.inner_label_checked.is_some() {
|
checked,
|
||||||
let inner_label = props.inner_label.as_deref().unwrap_or_default();
|
disabled,
|
||||||
let inner_label_checked = props.inner_label_checked.as_ref();
|
inline,
|
||||||
html! {
|
large,
|
||||||
<>
|
onclick,
|
||||||
<div class={classes!("bp3-control-indicator-child")}>
|
label,
|
||||||
<div class={classes!("bp3-switch-inner-text")}>
|
class,
|
||||||
{
|
inner_label_checked,
|
||||||
if let Some(label_checked) = inner_label_checked {
|
inner_label,
|
||||||
label_checked.clone()
|
align_right,
|
||||||
} else {
|
}: &SwitchProps,
|
||||||
inner_label.to_string()
|
) -> Html {
|
||||||
}
|
let display_label = (inner_label.is_some() || inner_label_checked.is_some()).then(|| {
|
||||||
|
let inner_label = inner_label.clone().unwrap_or_default();
|
||||||
|
|
||||||
|
html! {
|
||||||
|
<>
|
||||||
|
<div class={classes!("bp3-control-indicator-child")}>
|
||||||
|
<div class={classes!("bp3-switch-inner-text")}>
|
||||||
|
{
|
||||||
|
if let Some(label_checked) = inner_label_checked.clone() {
|
||||||
|
label_checked
|
||||||
|
} else {
|
||||||
|
inner_label.clone()
|
||||||
}
|
}
|
||||||
</div>
|
}
|
||||||
</div>
|
</div>
|
||||||
<div class={classes!("bp3-control-indicator-child")}>
|
</div>
|
||||||
<div class={classes!("bp3-switch-inner-text")}>
|
<div class={classes!("bp3-control-indicator-child")}>
|
||||||
{inner_label.to_string()}
|
<div class={classes!("bp3-switch-inner-text")}>
|
||||||
</div>
|
{inner_label}
|
||||||
</div>
|
</div>
|
||||||
</>
|
</div>
|
||||||
}
|
</>
|
||||||
} else {
|
|
||||||
Html::default()
|
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
|
|
||||||
html! {
|
html! {
|
||||||
<label
|
<label
|
||||||
class={classes!(
|
class={classes!(
|
||||||
"bp3-control",
|
"bp3-control",
|
||||||
"bp3-switch",
|
"bp3-switch",
|
||||||
props.disabled.then_some("bp3-disabled"),
|
disabled.then_some("bp3-disabled"),
|
||||||
props.inline.then_some("bp3-inline"),
|
inline.then_some("bp3-inline"),
|
||||||
props.large.then_some("bp3-large"),
|
large.then_some("bp3-large"),
|
||||||
props.class.clone(),
|
if *align_right {
|
||||||
if props.align_right {
|
|
||||||
"bp3-align-right"
|
"bp3-align-right"
|
||||||
} else {
|
} else {
|
||||||
"bp3-align-left"
|
"bp3-align-left"
|
||||||
},
|
},
|
||||||
|
class.clone(),
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={props.checked}
|
checked={*checked}
|
||||||
onclick={props.onclick.clone()}
|
{onclick}
|
||||||
disabled={props.disabled}
|
disabled={*disabled}
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
class={classes!("bp3-control-indicator")}
|
class={classes!("bp3-control-indicator")}
|
||||||
>
|
>
|
||||||
{display_label}
|
{display_label}
|
||||||
</span>
|
</span>
|
||||||
{props.label.clone()}
|
{label.clone()}
|
||||||
</label>
|
</label>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
13
src/tabs.rs
13
src/tabs.rs
|
@ -1,22 +1,23 @@
|
||||||
|
use implicit_clone::{unsync::IArray, ImplicitClone};
|
||||||
use std::collections::{hash_map::DefaultHasher, HashMap};
|
use std::collections::{hash_map::DefaultHasher, HashMap};
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use web_sys::HtmlElement;
|
use web_sys::HtmlElement;
|
||||||
use yew::prelude::*;
|
use yew::prelude::*;
|
||||||
|
|
||||||
pub struct Tabs<T: Clone + PartialEq + Hash + 'static> {
|
pub struct Tabs<T: ImplicitClone + PartialEq + Hash + 'static> {
|
||||||
tab_refs: HashMap<u64, NodeRef>,
|
tab_refs: HashMap<u64, NodeRef>,
|
||||||
indicator_ref: NodeRef,
|
indicator_ref: NodeRef,
|
||||||
phantom: PhantomData<T>,
|
phantom: PhantomData<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Properties)]
|
#[derive(Clone, PartialEq, Properties)]
|
||||||
pub struct TabsProps<T: Clone + PartialEq> {
|
pub struct TabsProps<T: ImplicitClone + PartialEq + 'static> {
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
pub animate: bool,
|
pub animate: bool,
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
pub default_selected_tab_id: Option<T>,
|
pub default_selected_tab_id: Option<T>,
|
||||||
pub id: String,
|
pub id: AttrValue,
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
pub large: bool,
|
pub large: bool,
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
|
@ -28,10 +29,10 @@ pub struct TabsProps<T: Clone + PartialEq> {
|
||||||
pub onchange: Callback<T>,
|
pub onchange: Callback<T>,
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
pub class: Classes,
|
pub class: Classes,
|
||||||
pub tabs: Vec<Tab<T>>,
|
pub tabs: IArray<Tab<T>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Clone + PartialEq + Hash + 'static> Component for Tabs<T> {
|
impl<T: ImplicitClone + PartialEq + Hash + 'static> Component for Tabs<T> {
|
||||||
type Message = ();
|
type Message = ();
|
||||||
type Properties = TabsProps<T>;
|
type Properties = TabsProps<T>;
|
||||||
|
|
||||||
|
@ -194,3 +195,5 @@ pub struct Tab<T> {
|
||||||
pub title_class: Classes,
|
pub title_class: Classes,
|
||||||
pub panel_class: Classes,
|
pub panel_class: Classes,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T: ImplicitClone> ImplicitClone for Tab<T> {}
|
||||||
|
|
69
src/tag.rs
69
src/tag.rs
|
@ -5,7 +5,7 @@ use yew::virtual_dom::AttrValue;
|
||||||
#[derive(Clone, PartialEq, Properties)]
|
#[derive(Clone, PartialEq, Properties)]
|
||||||
pub struct TagProps {
|
pub struct TagProps {
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
pub children: html::Children,
|
pub children: Children,
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
// FIXME Not clear that this field has any effect without `interactive` on.
|
// FIXME Not clear that this field has any effect without `interactive` on.
|
||||||
pub active: bool,
|
pub active: bool,
|
||||||
|
@ -40,25 +40,44 @@ pub struct TagProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[function_component(Tag)]
|
#[function_component(Tag)]
|
||||||
pub fn tag(props: &TagProps) -> Html {
|
pub fn tag(
|
||||||
let icon = props.icon.map(|icon| {
|
TagProps {
|
||||||
|
children,
|
||||||
|
active,
|
||||||
|
fill,
|
||||||
|
icon,
|
||||||
|
intent,
|
||||||
|
interactive,
|
||||||
|
large,
|
||||||
|
minimal,
|
||||||
|
multiline,
|
||||||
|
onclick,
|
||||||
|
onremove,
|
||||||
|
right_icon,
|
||||||
|
round,
|
||||||
|
title,
|
||||||
|
class,
|
||||||
|
style,
|
||||||
|
}: &TagProps,
|
||||||
|
) -> Html {
|
||||||
|
let icon = icon.map(|icon| {
|
||||||
html! {
|
html! {
|
||||||
<Icon icon={icon} />
|
<Icon {icon} />
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let right_icon = props.right_icon.map(|icon| {
|
let right_icon = right_icon.map(|icon| {
|
||||||
html! {
|
html! {
|
||||||
<Icon icon={icon} />
|
<Icon {icon} />
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let remove_button = props.onremove.clone().map(|callback| {
|
let remove_button = onremove.clone().map(|onclick| {
|
||||||
html! {
|
html! {
|
||||||
<button
|
<button
|
||||||
class={classes!("bp3-tag-remove")}
|
class={classes!("bp3-tag-remove")}
|
||||||
onclick={callback}
|
{onclick}
|
||||||
tabindex={props.interactive.then_some("0")}
|
tabindex={interactive.then_some("0")}
|
||||||
>
|
>
|
||||||
<Icon icon={IconName::SmallCross} />
|
<Icon icon={IconName::SmallCross} />
|
||||||
</button>
|
</button>
|
||||||
|
@ -69,29 +88,29 @@ pub fn tag(props: &TagProps) -> Html {
|
||||||
<span
|
<span
|
||||||
class={classes!(
|
class={classes!(
|
||||||
"bp3-tag",
|
"bp3-tag",
|
||||||
props.intent,
|
intent,
|
||||||
props.active.then_some("bp3-active"),
|
active.then_some("bp3-active"),
|
||||||
props.fill.then_some("bp3-fill"),
|
fill.then_some("bp3-fill"),
|
||||||
props.interactive.then_some("bp3-interactive"),
|
interactive.then_some("bp3-interactive"),
|
||||||
props.large.then_some("bp3-large"),
|
large.then_some("bp3-large"),
|
||||||
props.minimal.then_some("bp3-minimal"),
|
minimal.then_some("bp3-minimal"),
|
||||||
props.round.then_some("bp3-round"),
|
round.then_some("bp3-round"),
|
||||||
props.class.clone(),
|
class.clone(),
|
||||||
)}
|
)}
|
||||||
style={props.style.clone()}
|
{style}
|
||||||
onclick={props.onclick.clone()}
|
{onclick}
|
||||||
>
|
>
|
||||||
{icon.unwrap_or_default()}
|
{icon}
|
||||||
<Text
|
<Text
|
||||||
class={classes!("bp3-fill")}
|
class={classes!("bp3-fill")}
|
||||||
ellipsize={!props.multiline}
|
ellipsize={!multiline}
|
||||||
title={props.title.clone()}
|
{title}
|
||||||
inline=true
|
inline=true
|
||||||
>
|
>
|
||||||
{props.children.clone()}
|
{children.clone()}
|
||||||
</Text>
|
</Text>
|
||||||
{right_icon.unwrap_or_default()}
|
{right_icon}
|
||||||
{remove_button.unwrap_or_default()}
|
{remove_button}
|
||||||
</span>
|
</span>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
23
src/text.rs
23
src/text.rs
|
@ -19,17 +19,26 @@ pub struct TextProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[function_component(Text)]
|
#[function_component(Text)]
|
||||||
pub fn text(props: &TextProps) -> Html {
|
pub fn text(
|
||||||
|
TextProps {
|
||||||
|
ellipsize,
|
||||||
|
children,
|
||||||
|
class,
|
||||||
|
inline,
|
||||||
|
title,
|
||||||
|
style,
|
||||||
|
}: &TextProps,
|
||||||
|
) -> Html {
|
||||||
html! {
|
html! {
|
||||||
<@{if props.inline { "span" } else { "div"}}
|
<@{if *inline { "span" } else { "div"}}
|
||||||
class={classes!(
|
class={classes!(
|
||||||
props.class.clone(),
|
ellipsize.then_some("bp3-text-overflow-ellipsis"),
|
||||||
props.ellipsize.then_some("bp3-text-overflow-ellipsis"),
|
class.clone(),
|
||||||
)}
|
)}
|
||||||
style={props.style.clone()}
|
{style}
|
||||||
title={props.title.clone()}
|
{title}
|
||||||
>
|
>
|
||||||
{props.children.clone()}
|
{children.clone()}
|
||||||
</@>
|
</@>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,10 +7,9 @@ pub struct TextAreaProps {
|
||||||
pub class: Classes,
|
pub class: Classes,
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
pub fill: bool,
|
pub fill: bool,
|
||||||
|
//TODO pub grow_vertically: bool,
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
pub grow_vertically: bool,
|
pub r#ref: NodeRef,
|
||||||
#[prop_or_default]
|
|
||||||
pub input_ref: NodeRef,
|
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
pub intent: Option<Intent>,
|
pub intent: Option<Intent>,
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
|
@ -18,23 +17,33 @@ pub struct TextAreaProps {
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
pub small: bool,
|
pub small: bool,
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
pub onchange: Option<Callback<Event>>,
|
pub onchange: Callback<Event>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[function_component(TextArea)]
|
#[function_component(TextArea)]
|
||||||
pub fn text_area(props: &TextAreaProps) -> Html {
|
pub fn text_area(
|
||||||
let classes = classes!(
|
TextAreaProps {
|
||||||
"bp3-input",
|
class,
|
||||||
props.intent,
|
fill,
|
||||||
props.class.clone(),
|
r#ref,
|
||||||
props.fill.then_some("bp3-fill"),
|
intent,
|
||||||
props.small.then_some("bp3-small"),
|
large,
|
||||||
props.large.then_some("bp3-large"),
|
small,
|
||||||
);
|
onchange,
|
||||||
|
}: &TextAreaProps,
|
||||||
|
) -> Html {
|
||||||
html! {
|
html! {
|
||||||
<textarea
|
<textarea
|
||||||
class={classes}
|
class={classes!(
|
||||||
onchange={props.onchange.clone()}
|
"bp3-input",
|
||||||
|
intent,
|
||||||
|
fill.then_some("bp3-fill"),
|
||||||
|
small.then_some("bp3-small"),
|
||||||
|
large.then_some("bp3-large"),
|
||||||
|
class.clone(),
|
||||||
|
)}
|
||||||
|
ref={r#ref}
|
||||||
|
{onchange}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
71
src/tree.rs
71
src/tree.rs
|
@ -1,6 +1,7 @@
|
||||||
use crate::collapse::Collapse;
|
use crate::collapse::Collapse;
|
||||||
use crate::icon::{Icon, IconName};
|
use crate::icon::{Icon, IconName};
|
||||||
use crate::Intent;
|
use crate::Intent;
|
||||||
|
use gloo::timers::callback::Timeout;
|
||||||
use id_tree::*;
|
use id_tree::*;
|
||||||
use std::cell::{Ref, RefCell, RefMut};
|
use std::cell::{Ref, RefCell, RefMut};
|
||||||
use std::collections::hash_map::DefaultHasher;
|
use std::collections::hash_map::DefaultHasher;
|
||||||
|
@ -18,7 +19,7 @@ pub struct TreeData<T> {
|
||||||
|
|
||||||
impl<T> PartialEq for TreeData<T> {
|
impl<T> PartialEq for TreeData<T> {
|
||||||
fn eq(&self, other: &Self) -> bool {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
self.version == other.version
|
Rc::ptr_eq(&self.tree, &other.tree) && self.version == other.version
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,12 +67,12 @@ pub struct NodeData<T> {
|
||||||
pub disabled: bool,
|
pub disabled: bool,
|
||||||
pub has_caret: bool,
|
pub has_caret: bool,
|
||||||
pub icon: Option<IconName>,
|
pub icon: Option<IconName>,
|
||||||
pub icon_color: Option<String>,
|
pub icon_color: Option<AttrValue>,
|
||||||
pub icon_intent: Option<Intent>,
|
pub icon_intent: Option<Intent>,
|
||||||
pub is_expanded: bool,
|
pub is_expanded: bool,
|
||||||
pub is_selected: bool,
|
pub is_selected: bool,
|
||||||
pub label: yew::virtual_dom::VNode,
|
pub label: Html,
|
||||||
pub secondary_label: Option<yew::virtual_dom::VNode>,
|
pub secondary_label: Option<Html>,
|
||||||
pub data: T,
|
pub data: T,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -148,12 +149,7 @@ impl<T: Clone + PartialEq + 'static> Component for Tree<T> {
|
||||||
|
|
||||||
// FIXME: The 'static bound here is probably wrong. Fix this at the end of PR.
|
// FIXME: The 'static bound here is probably wrong. Fix this at the end of PR.
|
||||||
impl<T: 'static + Clone + PartialEq> Tree<T> {
|
impl<T: 'static + Clone + PartialEq> Tree<T> {
|
||||||
fn render_children(
|
fn render_children(&self, ctx: &Context<Self>, node_id: &NodeId, depth: u32) -> Html {
|
||||||
&self,
|
|
||||||
ctx: &Context<Self>,
|
|
||||||
node_id: &NodeId,
|
|
||||||
depth: u32,
|
|
||||||
) -> yew::virtual_dom::VNode {
|
|
||||||
let tree = ctx.props().tree.borrow();
|
let tree = ctx.props().tree.borrow();
|
||||||
let node = tree.get(node_id).unwrap();
|
let node = tree.get(node_id).unwrap();
|
||||||
let children = node.children();
|
let children = node.children();
|
||||||
|
@ -207,6 +203,8 @@ impl<T: 'static + Clone + PartialEq> Tree<T> {
|
||||||
struct TreeNode {
|
struct TreeNode {
|
||||||
handler_caret_click: Callback<MouseEvent>,
|
handler_caret_click: Callback<MouseEvent>,
|
||||||
handler_click: Callback<MouseEvent>,
|
handler_click: Callback<MouseEvent>,
|
||||||
|
// NOTE: to prevent event bubbling, see https://github.com/yewstack/yew/issues/3041
|
||||||
|
callback_timeout: Option<Timeout>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Properties)]
|
#[derive(Clone, Properties)]
|
||||||
|
@ -215,16 +213,16 @@ struct TreeNodeProps {
|
||||||
disabled: bool,
|
disabled: bool,
|
||||||
has_caret: bool,
|
has_caret: bool,
|
||||||
icon: Option<IconName>,
|
icon: Option<IconName>,
|
||||||
icon_color: Option<String>,
|
icon_color: Option<AttrValue>,
|
||||||
icon_intent: Option<Intent>,
|
icon_intent: Option<Intent>,
|
||||||
is_expanded: bool,
|
is_expanded: bool,
|
||||||
is_selected: bool,
|
is_selected: bool,
|
||||||
label: yew::virtual_dom::VNode,
|
label: Html,
|
||||||
secondary_label: Option<yew::virtual_dom::VNode>,
|
secondary_label: Option<Html>,
|
||||||
on_collapse: Option<Callback<(NodeId, MouseEvent)>>,
|
on_collapse: Option<Callback<(NodeId, MouseEvent)>>,
|
||||||
on_expand: Option<Callback<(NodeId, MouseEvent)>>,
|
on_expand: Option<Callback<(NodeId, MouseEvent)>>,
|
||||||
onclick: Option<Callback<(NodeId, MouseEvent)>>,
|
onclick: Option<Callback<(NodeId, MouseEvent)>>,
|
||||||
children: html::Children,
|
children: yew::Children,
|
||||||
depth: u32,
|
depth: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -261,6 +259,7 @@ impl Component for TreeNode {
|
||||||
TreeNode {
|
TreeNode {
|
||||||
handler_caret_click: ctx.link().callback(TreeNodeMessage::CaretClick),
|
handler_caret_click: ctx.link().callback(TreeNodeMessage::CaretClick),
|
||||||
handler_click: ctx.link().callback(TreeNodeMessage::Click),
|
handler_click: ctx.link().callback(TreeNodeMessage::Click),
|
||||||
|
callback_timeout: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -269,21 +268,20 @@ impl Component for TreeNode {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let node_id = ctx.props().node_id.clone();
|
||||||
match msg {
|
match msg {
|
||||||
TreeNodeMessage::CaretClick(event) => {
|
TreeNodeMessage::CaretClick(event) => {
|
||||||
if ctx.props().is_expanded {
|
if ctx.props().is_expanded {
|
||||||
if let Some(on_collapse) = ctx.props().on_collapse.as_ref() {
|
if let Some(on_collapse) = ctx.props().on_collapse.clone() {
|
||||||
event.stop_propagation();
|
self.register_callback(on_collapse, (node_id, event));
|
||||||
on_collapse.emit((ctx.props().node_id.clone(), event));
|
|
||||||
}
|
}
|
||||||
} else if let Some(on_expand) = ctx.props().on_expand.as_ref() {
|
} else if let Some(on_expand) = ctx.props().on_expand.clone() {
|
||||||
event.stop_propagation();
|
self.register_callback(on_expand, (node_id, event));
|
||||||
on_expand.emit((ctx.props().node_id.clone(), event));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TreeNodeMessage::Click(event) => {
|
TreeNodeMessage::Click(event) => {
|
||||||
if let Some(onclick) = ctx.props().onclick.as_ref() {
|
if let Some(onclick) = ctx.props().onclick.clone() {
|
||||||
onclick.emit((ctx.props().node_id.clone(), event));
|
self.register_callback(onclick, (node_id, event));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -306,16 +304,16 @@ impl Component for TreeNode {
|
||||||
>
|
>
|
||||||
{
|
{
|
||||||
if ctx.props().has_caret {
|
if ctx.props().has_caret {
|
||||||
let mut class = Classes::from("bp3-tree-node-caret");
|
|
||||||
class.push(if ctx.props().is_expanded {
|
|
||||||
"bp3-tree-node-caret-open"
|
|
||||||
} else {
|
|
||||||
"bp3-tree-node-caret-closed"
|
|
||||||
});
|
|
||||||
|
|
||||||
html! {
|
html! {
|
||||||
<Icon
|
<Icon
|
||||||
class={classes!(class.to_string())}
|
class={classes!(
|
||||||
|
"bp3-tree-node-caret",
|
||||||
|
if ctx.props().is_expanded {
|
||||||
|
"bp3-tree-node-caret-open"
|
||||||
|
} else {
|
||||||
|
"bp3-tree-node-caret-closed"
|
||||||
|
},
|
||||||
|
)}
|
||||||
icon={IconName::ChevronRight}
|
icon={IconName::ChevronRight}
|
||||||
onclick={self.handler_caret_click.clone()}
|
onclick={self.handler_caret_click.clone()}
|
||||||
/>
|
/>
|
||||||
|
@ -353,4 +351,17 @@ impl Component for TreeNode {
|
||||||
</li>
|
</li>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn rendered(&mut self, _ctx: &Context<Self>, _first_render: bool) {
|
||||||
|
self.callback_timeout.take();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TreeNode {
|
||||||
|
fn register_callback<IN: 'static>(&mut self, callback: Callback<IN>, value: IN) {
|
||||||
|
if self.callback_timeout.is_none() {
|
||||||
|
self.callback_timeout
|
||||||
|
.replace(Timeout::new(0, move || callback.emit(value)));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ edition = "2021"
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
env_logger = "0.9"
|
env_logger = "0.10"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
xtask-wasm = "0.1.3"
|
xtask-wasm = "0.1.9"
|
||||||
yewprint-css = { path = "../yewprint-css" }
|
yewprint-css = { path = "../yewprint-css" }
|
||||||
|
|
|
@ -19,4 +19,4 @@ flate2 = "1"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
tar = "0.4"
|
tar = "0.4"
|
||||||
ureq = { version = "2.4", features = ["json"] }
|
ureq = { version = "2.5", features = ["json"] }
|
||||||
|
|
|
@ -14,8 +14,9 @@ crate-type = ["cdylib"]
|
||||||
# logging them with `console.error`. This is great for development, but requires
|
# logging them with `console.error`. This is great for development, but requires
|
||||||
# all the `std::fmt` and `std::panicking` infrastructure, so isn't great for
|
# all the `std::fmt` and `std::panicking` infrastructure, so isn't great for
|
||||||
# code size when deploying.
|
# code size when deploying.
|
||||||
console_error_panic_hook = { version = "0.1.6", optional = true }
|
console_error_panic_hook = { version = "0.1.7", optional = true }
|
||||||
gloo = "0.6"
|
gloo = "0.8"
|
||||||
|
implicit-clone = "0.3.3"
|
||||||
once_cell = "1.16"
|
once_cell = "1.16"
|
||||||
wasm-bindgen = "0.2"
|
wasm-bindgen = "0.2"
|
||||||
web-sys = { version = "0.3", features = ["Window", "MediaQueryList", "Event", "HtmlInputElement"] }
|
web-sys = { version = "0.3", features = ["Window", "MediaQueryList", "Event", "HtmlInputElement"] }
|
||||||
|
@ -25,13 +26,13 @@ web-sys = { version = "0.3", features = ["Window", "MediaQueryList", "Event", "H
|
||||||
#
|
#
|
||||||
# Unfortunately, `wee_alloc` requires nightly Rust when targeting wasm for now.
|
# Unfortunately, `wee_alloc` requires nightly Rust when targeting wasm for now.
|
||||||
wee_alloc = { version = "0.4.5", optional = true }
|
wee_alloc = { version = "0.4.5", optional = true }
|
||||||
yew = "0.19"
|
yew = { version = "0.20", features = ["csr"] }
|
||||||
yew-router = "0.16"
|
yew-router = "0.17"
|
||||||
yewprint = { path = ".." }
|
yewprint = { path = ".." }
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
build-data = "0.1.3"
|
build-data = "0.1.3"
|
||||||
syntect = "4.4.0"
|
syntect = "0.5.0"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
ureq = "2.4"
|
ureq = "2.5"
|
||||||
|
|
|
@ -34,7 +34,7 @@ fn main() {
|
||||||
.join(&path)
|
.join(&path)
|
||||||
.with_file_name("mod.rs.html");
|
.with_file_name("mod.rs.html");
|
||||||
let src =
|
let src =
|
||||||
syntect::html::highlighted_html_for_file(&path, syntax_set, theme).unwrap();
|
syntect::html::highlighted_snippet_for_file(&path, syntax_set, theme).unwrap();
|
||||||
|
|
||||||
let _ = std::fs::create_dir_all(dest_path.parent().unwrap());
|
let _ = std::fs::create_dir_all(dest_path.parent().unwrap());
|
||||||
fs::write(&dest_path, src.trim_end()).unwrap();
|
fs::write(&dest_path, src.trim_end()).unwrap();
|
||||||
|
|
|
@ -62,8 +62,8 @@ impl Component for App {
|
||||||
Msg::ToggleLight => self.dark_theme ^= true,
|
Msg::ToggleLight => self.dark_theme ^= true,
|
||||||
Msg::GoToMenu(event, doc_menu) => {
|
Msg::GoToMenu(event, doc_menu) => {
|
||||||
event.prevent_default();
|
event.prevent_default();
|
||||||
if let Some(history) = ctx.link().history() {
|
if let Some(navigator) = ctx.link().navigator() {
|
||||||
history.push(doc_menu);
|
navigator.push(&doc_menu);
|
||||||
} else {
|
} else {
|
||||||
gloo::console::warn!("Could not get history from Context");
|
gloo::console::warn!("Could not get history from Context");
|
||||||
}
|
}
|
||||||
|
@ -284,7 +284,7 @@ impl Component for App {
|
||||||
{{ navigation }}
|
{{ navigation }}
|
||||||
<main class={classes!("docs-content-wrapper")} role="main">
|
<main class={classes!("docs-content-wrapper")} role="main">
|
||||||
<div class={classes!("docs-page")}>
|
<div class={classes!("docs-page")}>
|
||||||
<Switch<DocMenu> render={Switch::render(switch)} />
|
<Switch<DocMenu> render={switch} />
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
|
@ -293,7 +293,7 @@ impl Component for App {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn switch(route: &DocMenu) -> Html {
|
fn switch(route: DocMenu) -> Html {
|
||||||
match route {
|
match route {
|
||||||
DocMenu::Button | DocMenu::Home => html! (<ButtonDoc />),
|
DocMenu::Button | DocMenu::Home => html! (<ButtonDoc />),
|
||||||
DocMenu::ButtonGroup => html! (<ButtonGroupDoc />),
|
DocMenu::ButtonGroup => html! (<ButtonGroupDoc />),
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use yew::prelude::*;
|
use yew::prelude::*;
|
||||||
use yewprint::Button;
|
use yewprint::{Button, Intent};
|
||||||
|
|
||||||
pub struct Example {
|
pub struct Example {
|
||||||
counter: i64,
|
counter: i64,
|
||||||
|
@ -15,6 +15,7 @@ pub struct ExampleProps {
|
||||||
pub large: bool,
|
pub large: bool,
|
||||||
pub active: bool,
|
pub active: bool,
|
||||||
pub disabled: bool,
|
pub disabled: bool,
|
||||||
|
pub intent: Option<Intent>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum Msg {
|
pub enum Msg {
|
||||||
|
@ -37,20 +38,33 @@ impl Component for Example {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn view(&self, ctx: &Context<Self>) -> Html {
|
fn view(&self, ctx: &Context<Self>) -> Html {
|
||||||
|
let Self::Properties {
|
||||||
|
minimal,
|
||||||
|
fill,
|
||||||
|
small,
|
||||||
|
outlined,
|
||||||
|
loading,
|
||||||
|
large,
|
||||||
|
active,
|
||||||
|
disabled,
|
||||||
|
intent,
|
||||||
|
} = &ctx.props();
|
||||||
|
|
||||||
html! {
|
html! {
|
||||||
<div>
|
<div>
|
||||||
<p>{"Counter: "}{self.counter}</p>
|
<p>{"Counter: "}{self.counter}</p>
|
||||||
<div>
|
<div>
|
||||||
<Button
|
<Button
|
||||||
onclick={ctx.link().callback(|_| Msg::AddOne)}
|
onclick={ctx.link().callback(|_| Msg::AddOne)}
|
||||||
minimal={ctx.props().minimal}
|
{minimal}
|
||||||
fill={ctx.props().fill}
|
{fill}
|
||||||
small={ctx.props().small}
|
{small}
|
||||||
outlined={ctx.props().outlined}
|
{outlined}
|
||||||
loading={ctx.props().loading}
|
{loading}
|
||||||
large={ctx.props().large}
|
{large}
|
||||||
active={ctx.props().active}
|
{active}
|
||||||
disabled={ctx.props().disabled}
|
{disabled}
|
||||||
|
{intent}
|
||||||
>
|
>
|
||||||
{"Add 1"}
|
{"Add 1"}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
|
@ -2,8 +2,9 @@ mod example;
|
||||||
|
|
||||||
use crate::ExampleContainer;
|
use crate::ExampleContainer;
|
||||||
use example::*;
|
use example::*;
|
||||||
|
use implicit_clone::unsync::IArray;
|
||||||
use yew::prelude::*;
|
use yew::prelude::*;
|
||||||
use yewprint::{Switch, H1, H5};
|
use yewprint::{HtmlSelect, Intent, Switch, H1, H5};
|
||||||
|
|
||||||
pub struct ButtonDoc {
|
pub struct ButtonDoc {
|
||||||
callback: Callback<ExampleProps>,
|
callback: Callback<ExampleProps>,
|
||||||
|
@ -26,6 +27,7 @@ impl Component for ButtonDoc {
|
||||||
large: false,
|
large: false,
|
||||||
active: false,
|
active: false,
|
||||||
disabled: false,
|
disabled: false,
|
||||||
|
intent: None,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -136,6 +138,20 @@ crate::build_example_prop_component! {
|
||||||
checked={ctx.props().example_props.disabled}
|
checked={ctx.props().example_props.disabled}
|
||||||
label={html!("Disabled")}
|
label={html!("Disabled")}
|
||||||
/>
|
/>
|
||||||
|
<HtmlSelect<Option<Intent>>
|
||||||
|
options={[
|
||||||
|
(None, "None".into()),
|
||||||
|
(Some(Intent::Primary), "Primary".into()),
|
||||||
|
(Some(Intent::Success), "Success".into()),
|
||||||
|
(Some(Intent::Warning), "Warning".into()),
|
||||||
|
(Some(Intent::Danger), "Danger".into()),
|
||||||
|
].into_iter().collect::<IArray<_>>()}
|
||||||
|
onchange={self.update_props(ctx, |props, intent| ExampleProps {
|
||||||
|
intent,
|
||||||
|
..props
|
||||||
|
})}
|
||||||
|
value={ctx.props().example_props.intent}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,12 +9,18 @@ pub struct ExampleProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[function_component(Example)]
|
#[function_component(Example)]
|
||||||
pub fn example(props: &ExampleProps) -> Html {
|
pub fn example(
|
||||||
|
ExampleProps {
|
||||||
|
intent,
|
||||||
|
show_icon,
|
||||||
|
show_title,
|
||||||
|
}: &ExampleProps,
|
||||||
|
) -> Html {
|
||||||
html! {
|
html! {
|
||||||
<Callout
|
<Callout
|
||||||
title={props.show_title.then_some("Visually important content")}
|
title={show_title.then_some("Visually important content")}
|
||||||
without_icon={!props.show_icon}
|
without_icon={!show_icon}
|
||||||
intent={props.intent}
|
{intent}
|
||||||
>
|
>
|
||||||
<p>{"The Callout element's background reflects its intent, if any."}</p>
|
<p>{"The Callout element's background reflects its intent, if any."}</p>
|
||||||
</Callout>
|
</Callout>
|
||||||
|
|
|
@ -2,6 +2,7 @@ mod example;
|
||||||
|
|
||||||
use crate::ExampleContainer;
|
use crate::ExampleContainer;
|
||||||
use example::*;
|
use example::*;
|
||||||
|
use implicit_clone::unsync::IArray;
|
||||||
use yew::prelude::*;
|
use yew::prelude::*;
|
||||||
use yewprint::{HtmlSelect, Intent, Switch, H1, H5};
|
use yewprint::{HtmlSelect, Intent, Switch, H1, H5};
|
||||||
|
|
||||||
|
@ -82,17 +83,18 @@ crate::build_example_prop_component! {
|
||||||
/>
|
/>
|
||||||
<p>{"Select intent:"}</p>
|
<p>{"Select intent:"}</p>
|
||||||
<HtmlSelect<Option<Intent>>
|
<HtmlSelect<Option<Intent>>
|
||||||
options={vec![
|
options={[
|
||||||
(None, "None".to_string()),
|
(None, "None".into()),
|
||||||
(Some(Intent::Primary), "Primary".to_string()),
|
(Some(Intent::Primary), "Primary".into()),
|
||||||
(Some(Intent::Success), "Success".to_string()),
|
(Some(Intent::Success), "Success".into()),
|
||||||
(Some(Intent::Warning), "Warning".to_string()),
|
(Some(Intent::Warning), "Warning".into()),
|
||||||
(Some(Intent::Danger), "Danger".to_string()),
|
(Some(Intent::Danger), "Danger".into()),
|
||||||
]}
|
].into_iter().collect::<IArray<_>>()}
|
||||||
onchange={self.update_props(ctx, |props, intent| ExampleProps {
|
onchange={self.update_props(ctx, |props, intent| ExampleProps {
|
||||||
intent,
|
intent,
|
||||||
..props
|
..props
|
||||||
})}
|
})}
|
||||||
|
value={ctx.props().example_props.intent}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -2,6 +2,7 @@ mod example;
|
||||||
|
|
||||||
use crate::ExampleContainer;
|
use crate::ExampleContainer;
|
||||||
use example::*;
|
use example::*;
|
||||||
|
use implicit_clone::unsync::IArray;
|
||||||
use yew::prelude::*;
|
use yew::prelude::*;
|
||||||
use yewprint::{Elevation, HtmlSelect, Switch, H1, H5};
|
use yewprint::{Elevation, HtmlSelect, Switch, H1, H5};
|
||||||
|
|
||||||
|
@ -73,13 +74,13 @@ crate::build_example_prop_component! {
|
||||||
/>
|
/>
|
||||||
<p>{"Elevation:"}</p>
|
<p>{"Elevation:"}</p>
|
||||||
<HtmlSelect<Elevation>
|
<HtmlSelect<Elevation>
|
||||||
options={vec![
|
options={[
|
||||||
(Elevation::Level0, "Level 0".to_string()),
|
(Elevation::Level0, "Level 0".into()),
|
||||||
(Elevation::Level1, "Level 1".to_string()),
|
(Elevation::Level1, "Level 1".into()),
|
||||||
(Elevation::Level2, "Level 2".to_string()),
|
(Elevation::Level2, "Level 2".into()),
|
||||||
(Elevation::Level3, "Level 3".to_string()),
|
(Elevation::Level3, "Level 3".into()),
|
||||||
(Elevation::Level4, "Level 4".to_string()),
|
(Elevation::Level4, "Level 4".into()),
|
||||||
]}
|
].into_iter().collect::<IArray<_>>()}
|
||||||
value={ctx.props().example_props.elevation}
|
value={ctx.props().example_props.elevation}
|
||||||
onchange={self.update_props(ctx, |props, elevation| ExampleProps {
|
onchange={self.update_props(ctx, |props, elevation| ExampleProps {
|
||||||
elevation,
|
elevation,
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use implicit_clone::{unsync::IArray, ImplicitClone};
|
||||||
use yew::prelude::*;
|
use yew::prelude::*;
|
||||||
use yewprint::{Button, ControlGroup, HtmlSelect, IconName, InputGroup};
|
use yewprint::{Button, ControlGroup, HtmlSelect, IconName, InputGroup};
|
||||||
|
|
||||||
|
@ -8,20 +9,20 @@ pub struct ExampleProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[function_component(Example)]
|
#[function_component(Example)]
|
||||||
pub fn example(props: &ExampleProps) -> Html {
|
pub fn example(ExampleProps { fill, vertical }: &ExampleProps) -> Html {
|
||||||
html! {
|
html! {
|
||||||
<ControlGroup
|
<ControlGroup
|
||||||
fill={props.fill}
|
{fill}
|
||||||
vertical={props.vertical}
|
{vertical}
|
||||||
>
|
>
|
||||||
<HtmlSelect<Option<Sorting>>
|
<HtmlSelect<Option<Sorting>>
|
||||||
options={vec![
|
options={[
|
||||||
(None, "Filter".to_string()),
|
(None, "Filter".into()),
|
||||||
(Some(Sorting::NameAscending), "Name - ascending".to_string()),
|
(Some(Sorting::NameAscending), "Name - ascending".into()),
|
||||||
(Some(Sorting::NameDescending), "Name - descending".to_string()),
|
(Some(Sorting::NameDescending), "Name - descending".into()),
|
||||||
(Some(Sorting::PriceAscending), "Price - ascending".to_string()),
|
(Some(Sorting::PriceAscending), "Price - ascending".into()),
|
||||||
(Some(Sorting::PriceDescending), "Price - descending".to_string()),
|
(Some(Sorting::PriceDescending), "Price - descending".into()),
|
||||||
]}
|
].into_iter().collect::<IArray<_>>()}
|
||||||
/>
|
/>
|
||||||
<InputGroup placeholder="Find filters..." />
|
<InputGroup placeholder="Find filters..." />
|
||||||
<Button icon={IconName::ArrowRight} />
|
<Button icon={IconName::ArrowRight} />
|
||||||
|
@ -36,3 +37,5 @@ pub enum Sorting {
|
||||||
PriceAscending,
|
PriceAscending,
|
||||||
PriceDescending,
|
PriceDescending,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ImplicitClone for Sorting {}
|
||||||
|
|
|
@ -11,10 +11,10 @@ pub enum Msg {
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Properties)]
|
#[derive(Clone, PartialEq, Properties)]
|
||||||
pub struct ExampleContainerProps {
|
pub struct ExampleContainerProps {
|
||||||
pub source: yew::virtual_dom::VNode,
|
pub source: Html,
|
||||||
pub children: html::Children,
|
pub children: Children,
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
pub props: Option<yew::virtual_dom::VNode>,
|
pub props: Option<Html>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Component for ExampleContainer {
|
impl Component for ExampleContainer {
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use implicit_clone::{unsync::IArray, ImplicitClone};
|
||||||
use yew::prelude::*;
|
use yew::prelude::*;
|
||||||
use yewprint::{HtmlSelect, Text};
|
use yewprint::{HtmlSelect, Text};
|
||||||
|
|
||||||
|
@ -31,7 +32,7 @@ impl Component for Example {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
fn changed(&mut self, ctx: &Context<Self>) -> bool {
|
fn changed(&mut self, ctx: &Context<Self>, _old_props: &Self::Properties) -> bool {
|
||||||
if self.reset != ctx.props().reset {
|
if self.reset != ctx.props().reset {
|
||||||
self.reset = ctx.props().reset;
|
self.reset = ctx.props().reset;
|
||||||
self.log_level = LogLevel::Info;
|
self.log_level = LogLevel::Info;
|
||||||
|
@ -43,14 +44,14 @@ impl Component for Example {
|
||||||
html! {
|
html! {
|
||||||
<div style="width: 400px; text-align: center;">
|
<div style="width: 400px; text-align: center;">
|
||||||
<HtmlSelect<LogLevel>
|
<HtmlSelect<LogLevel>
|
||||||
options={vec![
|
options={[
|
||||||
(LogLevel::Trace, "TRACE".to_string()),
|
(LogLevel::Trace, "TRACE".into()),
|
||||||
(LogLevel::Debug, "DEBUG".to_string()),
|
(LogLevel::Debug, "DEBUG".into()),
|
||||||
(LogLevel::Info, "INFO".to_string()),
|
(LogLevel::Info, "INFO".into()),
|
||||||
(LogLevel::Warn, "WARN".to_string()),
|
(LogLevel::Warn, "WARN".into()),
|
||||||
(LogLevel::Error, "ERROR".to_string()),
|
(LogLevel::Error, "ERROR".into()),
|
||||||
(LogLevel::Off, "OFF".to_string()),
|
(LogLevel::Off, "OFF".into()),
|
||||||
]}
|
].into_iter().collect::<IArray<_>>()}
|
||||||
minimal={ctx.props().minimal}
|
minimal={ctx.props().minimal}
|
||||||
fill={ctx.props().fill}
|
fill={ctx.props().fill}
|
||||||
disabled={ctx.props().disabled}
|
disabled={ctx.props().disabled}
|
||||||
|
@ -74,3 +75,5 @@ pub enum LogLevel {
|
||||||
Error,
|
Error,
|
||||||
Off,
|
Off,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ImplicitClone for LogLevel {}
|
||||||
|
|
|
@ -2,8 +2,8 @@ mod example;
|
||||||
|
|
||||||
use crate::ExampleContainer;
|
use crate::ExampleContainer;
|
||||||
use example::*;
|
use example::*;
|
||||||
|
use implicit_clone::unsync::IArray;
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use std::borrow::Cow;
|
|
||||||
use web_sys::HtmlInputElement;
|
use web_sys::HtmlInputElement;
|
||||||
use yew::prelude::*;
|
use yew::prelude::*;
|
||||||
use yewprint::{HtmlSelect, Icon, IconName, InputGroup, Intent, Slider, Text, H1, H5};
|
use yewprint::{HtmlSelect, Icon, IconName, InputGroup, Intent, Slider, Text, H1, H5};
|
||||||
|
@ -58,21 +58,24 @@ impl Component for IconDoc {
|
||||||
"bp3-code-block"
|
"bp3-code-block"
|
||||||
);
|
);
|
||||||
|
|
||||||
let search_string = &self.search_string.to_lowercase();
|
let search_string = self.search_string.to_lowercase();
|
||||||
let icon_list = ICON_LIST
|
let icon_list = ICON_LIST
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|(icon_name, icon)| {
|
.filter_map(|(icon_name, icon)| {
|
||||||
icon_name.contains(search_string).then_some(*icon).map(|x| {
|
icon_name
|
||||||
html! {
|
.contains(&search_string)
|
||||||
<div class={classes!("docs-icon-list-item")}>
|
.then_some(*icon)
|
||||||
<Icon
|
.map(|icon| {
|
||||||
icon={x}
|
html! {
|
||||||
icon_size=20
|
<div class={classes!("docs-icon-list-item")}>
|
||||||
/>
|
<Icon
|
||||||
<Text>{format!("{:?}", x)}</Text>
|
{icon}
|
||||||
</div>
|
icon_size=20
|
||||||
}
|
/>
|
||||||
})
|
<Text>{format!("{:?}", icon)}</Text>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
})
|
||||||
})
|
})
|
||||||
.collect::<Html>();
|
.collect::<Html>();
|
||||||
|
|
||||||
|
@ -101,7 +104,7 @@ impl Component for IconDoc {
|
||||||
value={self.search_string.clone()}
|
value={self.search_string.clone()}
|
||||||
oninput={ctx.link().callback(|e: InputEvent| {
|
oninput={ctx.link().callback(|e: InputEvent| {
|
||||||
let value = e.target_unchecked_into::<HtmlInputElement>().value();
|
let value = e.target_unchecked_into::<HtmlInputElement>().value();
|
||||||
IconDocMsg::SearchIcon(value)
|
IconDocMsg::SearchIcon(value.into())
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -118,7 +121,7 @@ crate::build_example_prop_component! {
|
||||||
fn view(&self, ctx: &Context<Self>) -> Html {
|
fn view(&self, ctx: &Context<Self>) -> Html {
|
||||||
let option_labels = (0..=100)
|
let option_labels = (0..=100)
|
||||||
.map(|x| (x, (x % 20 == 0).then(|| format!("{}", x).into())))
|
.map(|x| (x, (x % 20 == 0).then(|| format!("{}", x).into())))
|
||||||
.collect::<Vec<_>>();
|
.collect::<IArray<_>>();
|
||||||
|
|
||||||
html! {
|
html! {
|
||||||
<div>
|
<div>
|
||||||
|
@ -153,13 +156,13 @@ crate::build_example_prop_component! {
|
||||||
{"Select intent:"}
|
{"Select intent:"}
|
||||||
</p>
|
</p>
|
||||||
<HtmlSelect<Option<Intent>>
|
<HtmlSelect<Option<Intent>>
|
||||||
options={vec![
|
options={[
|
||||||
(None, "None".to_string()),
|
(None, "None".into()),
|
||||||
(Some(Intent::Primary), "Primary".to_string()),
|
(Some(Intent::Primary), "Primary".into()),
|
||||||
(Some(Intent::Success), "Success".to_string()),
|
(Some(Intent::Success), "Success".into()),
|
||||||
(Some(Intent::Warning), "Warning".to_string()),
|
(Some(Intent::Warning), "Warning".into()),
|
||||||
(Some(Intent::Danger), "Danger".to_string()),
|
(Some(Intent::Danger), "Danger".into()),
|
||||||
]}
|
].into_iter().collect::<IArray<_>>()}
|
||||||
value={self.example_props.intent}
|
value={self.example_props.intent}
|
||||||
onchange={self.update_props(ctx, |props, intent| ExampleProps {
|
onchange={self.update_props(ctx, |props, intent| ExampleProps {
|
||||||
intent,
|
intent,
|
||||||
|
@ -179,7 +182,7 @@ crate::build_example_prop_component! {
|
||||||
..props
|
..props
|
||||||
})}
|
})}
|
||||||
value_label={
|
value_label={
|
||||||
Cow::Owned(format!("{}", ctx.props().example_props.icon_size))
|
format!("{}", ctx.props().example_props.icon_size)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -97,7 +97,7 @@ impl Component for Example {
|
||||||
value={self.histogram_value.clone()}
|
value={self.histogram_value.clone()}
|
||||||
oninput={ctx.link().callback(|e: InputEvent| {
|
oninput={ctx.link().callback(|e: InputEvent| {
|
||||||
let value = e.target_unchecked_into::<HtmlInputElement>().value();
|
let value = e.target_unchecked_into::<HtmlInputElement>().value();
|
||||||
Msg::UpdateHistogram(value)
|
Msg::UpdateHistogram(value.into())
|
||||||
})}
|
})}
|
||||||
onkeydown={ctx.link().callback(|e: KeyboardEvent| {
|
onkeydown={ctx.link().callback(|e: KeyboardEvent| {
|
||||||
if e.key() == "Enter" { Msg::AddHistogramEntry } else { Msg::Noop }
|
if e.key() == "Enter" { Msg::AddHistogramEntry } else { Msg::Noop }
|
||||||
|
@ -114,7 +114,7 @@ impl Component for Example {
|
||||||
value={self.password_value.clone()}
|
value={self.password_value.clone()}
|
||||||
oninput={ctx.link().callback(|e: InputEvent| {
|
oninput={ctx.link().callback(|e: InputEvent| {
|
||||||
let value = e.target_unchecked_into::<HtmlInputElement>().value();
|
let value = e.target_unchecked_into::<HtmlInputElement>().value();
|
||||||
Msg::UpdatePassword(value)
|
Msg::UpdatePassword(value.into())
|
||||||
})}
|
})}
|
||||||
onkeydown={ctx.link().callback(|e: KeyboardEvent| {
|
onkeydown={ctx.link().callback(|e: KeyboardEvent| {
|
||||||
if e.key() == "Enter" { Msg::AddPasswordEntry } else { Msg::Noop }
|
if e.key() == "Enter" { Msg::AddPasswordEntry } else { Msg::Noop }
|
||||||
|
@ -138,7 +138,7 @@ impl Component for Example {
|
||||||
value={self.tags_value.clone()}
|
value={self.tags_value.clone()}
|
||||||
oninput={ctx.link().callback(|e: InputEvent| {
|
oninput={ctx.link().callback(|e: InputEvent| {
|
||||||
let value = e.target_unchecked_into::<HtmlInputElement>().value();
|
let value = e.target_unchecked_into::<HtmlInputElement>().value();
|
||||||
Msg::UpdateTags(value)
|
Msg::UpdateTags(value.into())
|
||||||
})}
|
})}
|
||||||
onkeydown={ctx.link().callback(|e: KeyboardEvent| {
|
onkeydown={ctx.link().callback(|e: KeyboardEvent| {
|
||||||
if e.key() == "Enter" { Msg::AddTagsEntry } else { Msg::Noop }
|
if e.key() == "Enter" { Msg::AddTagsEntry } else { Msg::Noop }
|
||||||
|
|
|
@ -42,7 +42,7 @@ pub use logo::*;
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! include_raw_html {
|
macro_rules! include_raw_html {
|
||||||
($file:expr $(, $class:expr)?) => {{
|
($file:expr $(, $class:expr)?) => {{
|
||||||
yew::virtual_dom::VNode::VRef(web_sys::Node::from({
|
Html::VRef(web_sys::Node::from({
|
||||||
let div = web_sys::window()
|
let div = web_sys::window()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.document()
|
.document()
|
||||||
|
@ -136,7 +136,8 @@ static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
|
||||||
pub fn run_app() -> Result<(), wasm_bindgen::JsValue> {
|
pub fn run_app() -> Result<(), wasm_bindgen::JsValue> {
|
||||||
#[cfg(feature = "console_error_panic_hook")]
|
#[cfg(feature = "console_error_panic_hook")]
|
||||||
console_error_panic_hook::set_once();
|
console_error_panic_hook::set_once();
|
||||||
yew::start_app::<app::AppRoot>();
|
|
||||||
|
yew::Renderer::<app::AppRoot>::new().render();
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@ pub struct ExampleProps {
|
||||||
pub disable_buttons: bool,
|
pub disable_buttons: bool,
|
||||||
pub buttons_on_the_left: bool,
|
pub buttons_on_the_left: bool,
|
||||||
pub left_icon: bool,
|
pub left_icon: bool,
|
||||||
|
pub intent: Option<Intent>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum Msg {
|
pub enum Msg {
|
||||||
|
@ -50,32 +51,45 @@ impl Component for Example {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn view(&self, ctx: &Context<Self>) -> Html {
|
fn view(&self, ctx: &Context<Self>) -> Html {
|
||||||
|
let Self::Properties {
|
||||||
|
fill,
|
||||||
|
disabled,
|
||||||
|
large,
|
||||||
|
disable_buttons,
|
||||||
|
buttons_on_the_left,
|
||||||
|
left_icon,
|
||||||
|
intent,
|
||||||
|
} = &ctx.props();
|
||||||
|
|
||||||
html! {
|
html! {
|
||||||
<>
|
<>
|
||||||
<NumericInput<i32>
|
<NumericInput<i32>
|
||||||
disabled={ctx.props().disabled}
|
{disabled}
|
||||||
fill={ctx.props().large}
|
{fill}
|
||||||
|
{large}
|
||||||
value={self.value}
|
value={self.value}
|
||||||
bounds={-105..}
|
bounds={-105..}
|
||||||
increment=10
|
increment=10
|
||||||
placeholder={String::from("Greater or equal to -105...")}
|
placeholder={String::from("Greater or equal to -105...")}
|
||||||
onchange={ctx.link().callback(|x| Msg::UpdateValue(x))}
|
onchange={ctx.link().callback(|x| Msg::UpdateValue(x))}
|
||||||
disable_buttons={ctx.props().disable_buttons}
|
{disable_buttons}
|
||||||
buttons_on_the_left={ctx.props().buttons_on_the_left}
|
{buttons_on_the_left}
|
||||||
left_icon={ctx.props().left_icon.then_some(IconName::Dollar)}
|
left_icon={left_icon.then_some(IconName::Dollar)}
|
||||||
|
{intent}
|
||||||
/>
|
/>
|
||||||
<NumericInput<i32>
|
<NumericInput<i32>
|
||||||
disabled={ctx.props().disabled}
|
{disabled}
|
||||||
fill={ctx.props().fill}
|
{fill}
|
||||||
large={ctx.props().large}
|
{large}
|
||||||
value={self.value_two}
|
value={self.value_two}
|
||||||
bounds={-10..=10}
|
bounds={-10..=10}
|
||||||
increment=1
|
increment=1
|
||||||
placeholder={String::from("Integer between -10 and 10")}
|
placeholder={String::from("Integer between -10 and 10")}
|
||||||
onchange={ctx.link().callback(|x| Msg::UpdateValueTwo(x))}
|
onchange={ctx.link().callback(|x| Msg::UpdateValueTwo(x))}
|
||||||
disable_buttons={ctx.props().disable_buttons}
|
{disable_buttons}
|
||||||
buttons_on_the_left={ctx.props().buttons_on_the_left}
|
{buttons_on_the_left}
|
||||||
left_icon={ctx.props().left_icon.then_some(IconName::Dollar)}
|
left_icon={left_icon.then_some(IconName::Dollar)}
|
||||||
|
{intent}
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
icon={IconName::Refresh}
|
icon={IconName::Refresh}
|
||||||
|
@ -84,8 +98,8 @@ impl Component for Example {
|
||||||
{"Reset at 4"}
|
{"Reset at 4"}
|
||||||
</Button>
|
</Button>
|
||||||
<Callout
|
<Callout
|
||||||
title={"Selected values"}
|
title="Selected values"
|
||||||
intent={Intent::Primary}
|
{intent}
|
||||||
>
|
>
|
||||||
<ul>
|
<ul>
|
||||||
<li>{self.value}</li>
|
<li>{self.value}</li>
|
||||||
|
|
|
@ -2,8 +2,9 @@ mod example;
|
||||||
|
|
||||||
use crate::ExampleContainer;
|
use crate::ExampleContainer;
|
||||||
use example::*;
|
use example::*;
|
||||||
|
use implicit_clone::unsync::IArray;
|
||||||
use yew::prelude::*;
|
use yew::prelude::*;
|
||||||
use yewprint::{Switch, H1, H5};
|
use yewprint::{HtmlSelect, Intent, Switch, H1, H5};
|
||||||
|
|
||||||
pub struct NumericInputDoc {
|
pub struct NumericInputDoc {
|
||||||
callback: Callback<ExampleProps>,
|
callback: Callback<ExampleProps>,
|
||||||
|
@ -24,6 +25,7 @@ impl Component for NumericInputDoc {
|
||||||
disable_buttons: false,
|
disable_buttons: false,
|
||||||
buttons_on_the_left: false,
|
buttons_on_the_left: false,
|
||||||
left_icon: false,
|
left_icon: false,
|
||||||
|
intent: None,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -116,6 +118,20 @@ crate::build_example_prop_component! {
|
||||||
checked={ctx.props().example_props.left_icon}
|
checked={ctx.props().example_props.left_icon}
|
||||||
label={html!("Left icon")}
|
label={html!("Left icon")}
|
||||||
/>
|
/>
|
||||||
|
<HtmlSelect<Option<Intent>>
|
||||||
|
options={[
|
||||||
|
(None, "None".into()),
|
||||||
|
(Some(Intent::Primary), "Primary".into()),
|
||||||
|
(Some(Intent::Success), "Success".into()),
|
||||||
|
(Some(Intent::Warning), "Warning".into()),
|
||||||
|
(Some(Intent::Danger), "Danger".into()),
|
||||||
|
].into_iter().collect::<IArray<_>>()}
|
||||||
|
onchange={self.update_props(ctx, |props, intent| ExampleProps {
|
||||||
|
intent,
|
||||||
|
..props
|
||||||
|
})}
|
||||||
|
value={ctx.props().example_props.intent}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ mod example;
|
||||||
|
|
||||||
use crate::ExampleContainer;
|
use crate::ExampleContainer;
|
||||||
use example::*;
|
use example::*;
|
||||||
|
use implicit_clone::unsync::IArray;
|
||||||
use yew::prelude::*;
|
use yew::prelude::*;
|
||||||
use yewprint::{HtmlSelect, Intent, Switch, H1, H5};
|
use yewprint::{HtmlSelect, Intent, Switch, H1, H5};
|
||||||
|
|
||||||
|
@ -82,13 +83,13 @@ crate::build_example_prop_component! {
|
||||||
/>
|
/>
|
||||||
<p>{"Select intent:"}</p>
|
<p>{"Select intent:"}</p>
|
||||||
<HtmlSelect<Option<Intent>>
|
<HtmlSelect<Option<Intent>>
|
||||||
options={vec![
|
options={[
|
||||||
(None, "None".to_string()),
|
(None, "None".into()),
|
||||||
(Some(Intent::Primary), "Primary".to_string()),
|
(Some(Intent::Primary), "Primary".into()),
|
||||||
(Some(Intent::Success), "Success".to_string()),
|
(Some(Intent::Success), "Success".into()),
|
||||||
(Some(Intent::Warning), "Warning".to_string()),
|
(Some(Intent::Warning), "Warning".into()),
|
||||||
(Some(Intent::Danger), "Danger".to_string()),
|
(Some(Intent::Danger), "Danger".into()),
|
||||||
]}
|
].into_iter().collect::<IArray<_>>()}
|
||||||
value={self.example_props.intent}
|
value={self.example_props.intent}
|
||||||
onchange={self.update_props(ctx, |props, intent| ExampleProps {
|
onchange={self.update_props(ctx, |props, intent| ExampleProps {
|
||||||
intent,
|
intent,
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use implicit_clone::{unsync::IArray, ImplicitClone};
|
||||||
use yew::prelude::*;
|
use yew::prelude::*;
|
||||||
use yewprint::{Label, Radio, RadioGroup};
|
use yewprint::{Label, Radio, RadioGroup};
|
||||||
|
|
||||||
|
@ -61,11 +62,11 @@ impl Component for Example {
|
||||||
{"Determine Lunch"}
|
{"Determine Lunch"}
|
||||||
</Label>
|
</Label>
|
||||||
))}
|
))}
|
||||||
options={vec![
|
options={[
|
||||||
(Lunch::Soup, "Soup".to_string()),
|
(Lunch::Soup, "Soup".into()),
|
||||||
(Lunch::Salad, "Salad".to_string()),
|
(Lunch::Salad, "Salad".into()),
|
||||||
(Lunch::Sandwich, "Sandwich".to_string()),
|
(Lunch::Sandwich, "Sandwich".into()),
|
||||||
]}
|
].into_iter().collect::<IArray<_>>()}
|
||||||
value={self.selected_value}
|
value={self.selected_value}
|
||||||
onchange={ctx.link().callback(|v| Msg::ValueUpdate(v))}
|
onchange={ctx.link().callback(|v| Msg::ValueUpdate(v))}
|
||||||
inline={ctx.props().inline}
|
inline={ctx.props().inline}
|
||||||
|
@ -84,3 +85,5 @@ pub enum Lunch {
|
||||||
Salad,
|
Salad,
|
||||||
Sandwich,
|
Sandwich,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ImplicitClone for Lunch {}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use std::borrow::Cow;
|
use implicit_clone::{unsync::IArray, ImplicitClone};
|
||||||
use yew::prelude::*;
|
use yew::prelude::*;
|
||||||
use yewprint::{Intent, Slider, Tag};
|
use yewprint::{Intent, Slider, Tag};
|
||||||
|
|
||||||
|
@ -53,7 +53,7 @@ impl Component for Example {
|
||||||
let percentage_labels = (0..=100)
|
let percentage_labels = (0..=100)
|
||||||
.step_by(1)
|
.step_by(1)
|
||||||
.map(|x| (x, (x % 10 == 0).then(|| format!("{}%", x).into())))
|
.map(|x| (x, (x % 10 == 0).then(|| format!("{}%", x).into())))
|
||||||
.collect::<Vec<_>>();
|
.collect::<IArray<_>>();
|
||||||
|
|
||||||
html! {
|
html! {
|
||||||
<>
|
<>
|
||||||
|
@ -62,7 +62,7 @@ impl Component for Example {
|
||||||
>
|
>
|
||||||
<Slider<f64>
|
<Slider<f64>
|
||||||
selected={self.float}
|
selected={self.float}
|
||||||
values={vec![
|
values={[
|
||||||
(0.0, Some("0".into())),
|
(0.0, Some("0".into())),
|
||||||
(0.1, None),
|
(0.1, None),
|
||||||
(0.2, None),
|
(0.2, None),
|
||||||
|
@ -74,7 +74,7 @@ impl Component for Example {
|
||||||
(0.8, None),
|
(0.8, None),
|
||||||
(0.9, None),
|
(0.9, None),
|
||||||
(1.0, Some("1".into())),
|
(1.0, Some("1".into())),
|
||||||
]}
|
].into_iter().collect::<IArray<_>>()}
|
||||||
intent={ctx.props().intent}
|
intent={ctx.props().intent}
|
||||||
onchange={ctx.link().callback(|x| Msg::FloatUpdate(x))}
|
onchange={ctx.link().callback(|x| Msg::FloatUpdate(x))}
|
||||||
/>
|
/>
|
||||||
|
@ -90,24 +90,26 @@ impl Component for Example {
|
||||||
values={percentage_labels}
|
values={percentage_labels}
|
||||||
selected={self.integer}
|
selected={self.integer}
|
||||||
intent={ctx.props().intent}
|
intent={ctx.props().intent}
|
||||||
value_label={Cow::Owned(format!("{}%", self.integer))}
|
value_label={format!("{}%", self.integer)}
|
||||||
onchange={ctx.link().callback(|x| Msg::IntegerUpdate(x))}
|
onchange={ctx.link().callback(|x| Msg::IntegerUpdate(x))}
|
||||||
/>
|
/>
|
||||||
<Slider<LogLevel>
|
<Slider<LogLevel>
|
||||||
values={vec![
|
values={[
|
||||||
(LogLevel::Off, Some("OFF".into())),
|
(LogLevel::Off, Some("OFF".into())),
|
||||||
(LogLevel::Error, Some("ERROR".into())),
|
(LogLevel::Error, Some("ERROR".into())),
|
||||||
(LogLevel::Warn, Some("WARN".into())),
|
(LogLevel::Warn, Some("WARN".into())),
|
||||||
(LogLevel::Info, Some("INFO".into())),
|
(LogLevel::Info, Some("INFO".into())),
|
||||||
(LogLevel::Debug, Some("DEBUG".into())),
|
(LogLevel::Debug, Some("DEBUG".into())),
|
||||||
(LogLevel::Trace, Some("TRACE".into())),
|
(LogLevel::Trace, Some("TRACE".into())),
|
||||||
]}
|
].into_iter().collect::<IArray<_>>()}
|
||||||
intent={ctx.props().intent}
|
intent={ctx.props().intent}
|
||||||
selected={self.log_level}
|
selected={self.log_level}
|
||||||
onchange={ctx.link().callback(|x| Msg::LogLevelUpdate(x))}
|
onchange={ctx.link().callback(|x| Msg::LogLevelUpdate(x))}
|
||||||
/>
|
/>
|
||||||
<Slider<()>
|
<Slider<()>
|
||||||
values={vec![((), Some("Neo".into()))]}
|
values={[
|
||||||
|
((), Some("Neo".into()))
|
||||||
|
].into_iter().collect::<IArray<_>>()}
|
||||||
intent={ctx.props().intent}
|
intent={ctx.props().intent}
|
||||||
selected={()}
|
selected={()}
|
||||||
onchange={ctx.link().callback(|_| Msg::Noop)}
|
onchange={ctx.link().callback(|_| Msg::Noop)}
|
||||||
|
@ -126,3 +128,5 @@ pub enum LogLevel {
|
||||||
Error,
|
Error,
|
||||||
Off,
|
Off,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ImplicitClone for LogLevel {}
|
||||||
|
|
|
@ -2,6 +2,7 @@ mod example;
|
||||||
|
|
||||||
use crate::ExampleContainer;
|
use crate::ExampleContainer;
|
||||||
use example::*;
|
use example::*;
|
||||||
|
use implicit_clone::unsync::IArray;
|
||||||
use yew::prelude::*;
|
use yew::prelude::*;
|
||||||
use yewprint::{HtmlSelect, Intent, Switch, H1, H2, H5};
|
use yewprint::{HtmlSelect, Intent, Switch, H1, H2, H5};
|
||||||
|
|
||||||
|
@ -119,17 +120,18 @@ crate::build_example_prop_component! {
|
||||||
/>
|
/>
|
||||||
<p>{"Select intent:"}</p>
|
<p>{"Select intent:"}</p>
|
||||||
<HtmlSelect<Option<Intent>>
|
<HtmlSelect<Option<Intent>>
|
||||||
options={vec![
|
options={[
|
||||||
(None, "None".to_string()),
|
(None, "None".into()),
|
||||||
(Some(Intent::Primary), "Primary".to_string()),
|
(Some(Intent::Primary), "Primary".into()),
|
||||||
(Some(Intent::Success), "Success".to_string()),
|
(Some(Intent::Success), "Success".into()),
|
||||||
(Some(Intent::Warning), "Warning".to_string()),
|
(Some(Intent::Warning), "Warning".into()),
|
||||||
(Some(Intent::Danger), "Danger".to_string()),
|
(Some(Intent::Danger), "Danger".into()),
|
||||||
]}
|
].into_iter().collect::<IArray<_>>()}
|
||||||
onchange={self.update_props(ctx, |props, intent| ExampleProps {
|
onchange={self.update_props(ctx, |props, intent| ExampleProps {
|
||||||
intent,
|
intent,
|
||||||
..props
|
..props
|
||||||
})}
|
})}
|
||||||
|
value={ctx.props().example_props.intent}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ mod example;
|
||||||
|
|
||||||
use crate::ExampleContainer;
|
use crate::ExampleContainer;
|
||||||
use example::*;
|
use example::*;
|
||||||
|
use implicit_clone::unsync::IArray;
|
||||||
use yew::prelude::*;
|
use yew::prelude::*;
|
||||||
use yewprint::{HtmlSelect, Intent, Slider, H1, H5};
|
use yewprint::{HtmlSelect, Intent, Slider, H1, H5};
|
||||||
|
|
||||||
|
@ -67,17 +68,18 @@ crate::build_example_prop_component! {
|
||||||
<div>
|
<div>
|
||||||
<p>{"Select intent:"}</p>
|
<p>{"Select intent:"}</p>
|
||||||
<HtmlSelect<Option<Intent>>
|
<HtmlSelect<Option<Intent>>
|
||||||
options={vec![
|
options={[
|
||||||
(None, "None".to_string()),
|
(None, "None".into()),
|
||||||
(Some(Intent::Primary), "Primary".to_string()),
|
(Some(Intent::Primary), "Primary".into()),
|
||||||
(Some(Intent::Success), "Success".to_string()),
|
(Some(Intent::Success), "Success".into()),
|
||||||
(Some(Intent::Warning), "Warning".to_string()),
|
(Some(Intent::Warning), "Warning".into()),
|
||||||
(Some(Intent::Danger), "Danger".to_string()),
|
(Some(Intent::Danger), "Danger".into()),
|
||||||
]}
|
].into_iter().collect::<IArray<_>>()}
|
||||||
onchange={self.update_props(ctx, |props, intent| ExampleProps {
|
onchange={self.update_props(ctx, |props, intent| ExampleProps {
|
||||||
intent,
|
intent,
|
||||||
..props
|
..props
|
||||||
})}
|
})}
|
||||||
|
value={ctx.props().example_props.intent}
|
||||||
/>
|
/>
|
||||||
<p
|
<p
|
||||||
style="margin-top: 5px;"
|
style="margin-top: 5px;"
|
||||||
|
@ -86,7 +88,7 @@ crate::build_example_prop_component! {
|
||||||
</p>
|
</p>
|
||||||
<Slider<u32>
|
<Slider<u32>
|
||||||
selected={ctx.props().example_props.size}
|
selected={ctx.props().example_props.size}
|
||||||
values={vec![
|
values={[
|
||||||
(10, Some("10".into())),
|
(10, Some("10".into())),
|
||||||
(20, None),
|
(20, None),
|
||||||
(30, None),
|
(30, None),
|
||||||
|
@ -97,7 +99,7 @@ crate::build_example_prop_component! {
|
||||||
(80, None),
|
(80, None),
|
||||||
(90, None),
|
(90, None),
|
||||||
(100, Some("100".into())),
|
(100, Some("100".into())),
|
||||||
]}
|
].into_iter().collect::<IArray<_>>()}
|
||||||
onchange={self.update_props(ctx, |props, size| ExampleProps {
|
onchange={self.update_props(ctx, |props, size| ExampleProps {
|
||||||
size,
|
size,
|
||||||
..props
|
..props
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use implicit_clone::{unsync::IArray, ImplicitClone};
|
||||||
use yew::prelude::*;
|
use yew::prelude::*;
|
||||||
use yewprint::{Tab, Tabs};
|
use yewprint::{Tab, Tabs};
|
||||||
|
|
||||||
|
@ -39,7 +40,7 @@ impl Component for Example {
|
||||||
vertical={ctx.props().vertical}
|
vertical={ctx.props().vertical}
|
||||||
selected_tab_id={self.selected}
|
selected_tab_id={self.selected}
|
||||||
onchange={ctx.link().callback(|x| x)}
|
onchange={ctx.link().callback(|x| x)}
|
||||||
tabs={vec![
|
tabs={[
|
||||||
Tab {
|
Tab {
|
||||||
disabled: false,
|
disabled: false,
|
||||||
id: Civilization::Sumer,
|
id: Civilization::Sumer,
|
||||||
|
@ -120,7 +121,7 @@ impl Component for Example {
|
||||||
panel_class: Classes::default(),
|
panel_class: Classes::default(),
|
||||||
title_class: Classes::default(),
|
title_class: Classes::default(),
|
||||||
},
|
},
|
||||||
]}
|
].into_iter().collect::<IArray<_>>()}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
@ -134,3 +135,5 @@ pub enum Civilization {
|
||||||
AncientEgypt,
|
AncientEgypt,
|
||||||
IndusValley,
|
IndusValley,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ImplicitClone for Civilization {}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use implicit_clone::unsync::IArray;
|
||||||
use yew::prelude::*;
|
use yew::prelude::*;
|
||||||
use yewprint::{IconName, Intent, Tag};
|
use yewprint::{IconName, Intent, Tag};
|
||||||
|
|
||||||
|
@ -7,7 +8,7 @@ pub struct Example {
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Properties)]
|
#[derive(Clone, PartialEq, Properties)]
|
||||||
pub struct ExampleProps {
|
pub struct ExampleProps {
|
||||||
pub initial_tags: Vec<String>,
|
pub initial_tags: IArray<AttrValue>,
|
||||||
pub active: bool,
|
pub active: bool,
|
||||||
pub fill: bool,
|
pub fill: bool,
|
||||||
pub icon: bool,
|
pub icon: bool,
|
||||||
|
@ -32,7 +33,12 @@ impl Component for Example {
|
||||||
type Properties = ExampleProps;
|
type Properties = ExampleProps;
|
||||||
|
|
||||||
fn create(ctx: &Context<Self>) -> Self {
|
fn create(ctx: &Context<Self>) -> Self {
|
||||||
let tags = ctx.props().initial_tags.clone();
|
let tags = ctx
|
||||||
|
.props()
|
||||||
|
.initial_tags
|
||||||
|
.iter()
|
||||||
|
.map(|x| x.to_string())
|
||||||
|
.collect::<Vec<_>>();
|
||||||
Example { tags }
|
Example { tags }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,9 +57,14 @@ impl Component for Example {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
fn changed(&mut self, ctx: &Context<Self>) -> bool {
|
fn changed(&mut self, ctx: &Context<Self>, _old_props: &Self::Properties) -> bool {
|
||||||
if ctx.props().reset_tags != ctx.props().reset_tags {
|
if ctx.props().reset_tags != ctx.props().reset_tags {
|
||||||
self.tags = ctx.props().initial_tags.clone();
|
self.tags = ctx
|
||||||
|
.props()
|
||||||
|
.initial_tags
|
||||||
|
.iter()
|
||||||
|
.map(|x| x.to_string())
|
||||||
|
.collect::<Vec<_>>();
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ mod example;
|
||||||
|
|
||||||
use crate::ExampleContainer;
|
use crate::ExampleContainer;
|
||||||
use example::*;
|
use example::*;
|
||||||
|
use implicit_clone::unsync::{IArray, IString};
|
||||||
use yew::prelude::*;
|
use yew::prelude::*;
|
||||||
use yewprint::{Button, ButtonGroup, HtmlSelect, IconName, Intent, Switch, H1, H5};
|
use yewprint::{Button, ButtonGroup, HtmlSelect, IconName, Intent, Switch, H1, H5};
|
||||||
|
|
||||||
|
@ -10,8 +11,8 @@ pub struct TagDoc {
|
||||||
state: ExampleProps,
|
state: ExampleProps,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn initial_tags() -> Vec<String> {
|
fn initial_tags() -> IArray<AttrValue> {
|
||||||
vec![
|
[
|
||||||
"Landscape".into(),
|
"Landscape".into(),
|
||||||
"Bird".into(),
|
"Bird".into(),
|
||||||
"City".into(),
|
"City".into(),
|
||||||
|
@ -22,6 +23,8 @@ fn initial_tags() -> Vec<String> {
|
||||||
too. Coming back to where you started is not the same as never leaving."
|
too. Coming back to where you started is not the same as never leaving."
|
||||||
.into(),
|
.into(),
|
||||||
]
|
]
|
||||||
|
.into_iter()
|
||||||
|
.collect::<IArray<_>>()
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Component for TagDoc {
|
impl Component for TagDoc {
|
||||||
|
@ -173,17 +176,18 @@ crate::build_example_prop_component! {
|
||||||
vertical=true
|
vertical=true
|
||||||
>
|
>
|
||||||
<HtmlSelect<Option<Intent>>
|
<HtmlSelect<Option<Intent>>
|
||||||
options={vec![
|
options={IArray::Static(&[
|
||||||
(None, "None".to_string()),
|
(None, IString::Static("None")),
|
||||||
(Some(Intent::Primary), "Primary".to_string()),
|
(Some(Intent::Primary), IString::Static("Primary")),
|
||||||
(Some(Intent::Success), "Success".to_string()),
|
(Some(Intent::Success), IString::Static("Success")),
|
||||||
(Some(Intent::Warning), "Warning".to_string()),
|
(Some(Intent::Warning), IString::Static("Warning")),
|
||||||
(Some(Intent::Danger), "Danger".to_string()),
|
(Some(Intent::Danger), IString::Static("Danger")),
|
||||||
]}
|
])}
|
||||||
onchange={self.update_props(ctx, |props, intent| ExampleProps {
|
onchange={self.update_props(ctx, |props, intent| ExampleProps {
|
||||||
intent,
|
intent,
|
||||||
..props
|
..props
|
||||||
})}
|
})}
|
||||||
|
value={ctx.props().example_props.intent}
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
icon={IconName::Refresh}
|
icon={IconName::Refresh}
|
||||||
|
|
|
@ -4,7 +4,7 @@ use yewprint::Text;
|
||||||
#[derive(Clone, PartialEq, Properties)]
|
#[derive(Clone, PartialEq, Properties)]
|
||||||
pub struct ExampleProps {
|
pub struct ExampleProps {
|
||||||
pub ellipsize: bool,
|
pub ellipsize: bool,
|
||||||
pub text: String,
|
pub text: AttrValue,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[function_component(Example)]
|
#[function_component(Example)]
|
||||||
|
|
|
@ -20,7 +20,7 @@ impl Component for TextDoc {
|
||||||
callback: ctx.link().callback(|x| x),
|
callback: ctx.link().callback(|x| x),
|
||||||
state: ExampleProps {
|
state: ExampleProps {
|
||||||
ellipsize: false,
|
ellipsize: false,
|
||||||
text: String::from("Hello, world!"),
|
text: "Hello, world!".into(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -78,12 +78,12 @@ crate::build_example_prop_component! {
|
||||||
onchange={self.update_props(ctx, |props, e: Event| {
|
onchange={self.update_props(ctx, |props, e: Event| {
|
||||||
if let Some(input) = e.target_dyn_into::<HtmlInputElement>() {
|
if let Some(input) = e.target_dyn_into::<HtmlInputElement>() {
|
||||||
ExampleProps {
|
ExampleProps {
|
||||||
text: input.value(),
|
text: input.value().into(),
|
||||||
..props
|
..props
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ExampleProps {
|
ExampleProps {
|
||||||
text: "Hello, world!".to_string(),
|
text: "Hello, world!".into(),
|
||||||
..props
|
..props
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ mod example;
|
||||||
|
|
||||||
use crate::ExampleContainer;
|
use crate::ExampleContainer;
|
||||||
use example::*;
|
use example::*;
|
||||||
|
use implicit_clone::unsync::IArray;
|
||||||
use yew::prelude::*;
|
use yew::prelude::*;
|
||||||
use yewprint::{HtmlSelect, Intent, Switch, H1, H5};
|
use yewprint::{HtmlSelect, Intent, Switch, H1, H5};
|
||||||
|
|
||||||
|
@ -91,17 +92,18 @@ crate::build_example_prop_component! {
|
||||||
label={html!("Small")}
|
label={html!("Small")}
|
||||||
/>
|
/>
|
||||||
<HtmlSelect<Option<Intent>>
|
<HtmlSelect<Option<Intent>>
|
||||||
options={vec![
|
options={[
|
||||||
(None, "None".to_string()),
|
(None, "None".into()),
|
||||||
(Some(Intent::Primary), "Primary".to_string()),
|
(Some(Intent::Primary), "Primary".into()),
|
||||||
(Some(Intent::Success), "Success".to_string()),
|
(Some(Intent::Success), "Success".into()),
|
||||||
(Some(Intent::Warning), "Warning".to_string()),
|
(Some(Intent::Warning), "Warning".into()),
|
||||||
(Some(Intent::Danger), "Danger".to_string()),
|
(Some(Intent::Danger), "Danger".into()),
|
||||||
]}
|
].into_iter().collect::<IArray<_>>()}
|
||||||
onchange={self.update_props(ctx, |props, intent| ExampleProps {
|
onchange={self.update_props(ctx, |props, intent| ExampleProps {
|
||||||
intent,
|
intent,
|
||||||
..props
|
..props
|
||||||
})}
|
})}
|
||||||
|
value={ctx.props().example_props.intent}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue