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