mirror of
https://github.com/yewprint/yewprint
synced 2024-11-25 13:00:19 +00:00
Spinner (#96)
This commit is contained in:
parent
c9b23d91e6
commit
24ae8f898d
8 changed files with 269 additions and 8 deletions
|
@ -10,6 +10,7 @@ use crate::icon::*;
|
|||
use crate::input_group::*;
|
||||
use crate::menu::*;
|
||||
use crate::progressbar::*;
|
||||
use crate::spinner::*;
|
||||
use crate::switch::*;
|
||||
use crate::tabs::*;
|
||||
use crate::tag::*;
|
||||
|
@ -183,6 +184,12 @@ impl Component for App {
|
|||
onclick=self.link
|
||||
.callback(|_| Msg::GoToMenu(DocMenu::ProgressBar))
|
||||
/>
|
||||
<MenuItem
|
||||
text={html!("Spinner")}
|
||||
href="#spinner"
|
||||
onclick=self.link
|
||||
.callback(|_| Msg::GoToMenu(DocMenu::Spinner))
|
||||
/>
|
||||
<MenuItem
|
||||
text={html!("Switch")}
|
||||
href="#switch"
|
||||
|
@ -243,6 +250,7 @@ impl Component for App {
|
|||
DocMenu::InputGroup => html!(<InputGroupDoc />),
|
||||
DocMenu::Menu => html!(<MenuDoc />),
|
||||
DocMenu::ProgressBar => html!(<ProgressBarDoc />),
|
||||
DocMenu::Spinner => html!(<SpinnerDoc />),
|
||||
DocMenu::Switch => html!(<SwitchDoc />),
|
||||
DocMenu::Tabs => html!(<TabsDoc />),
|
||||
DocMenu::Tag => html!(<TagDoc />),
|
||||
|
@ -285,6 +293,8 @@ pub enum DocMenu {
|
|||
Menu,
|
||||
#[to = "/#progress-bar"]
|
||||
ProgressBar,
|
||||
#[to = "/#spinner"]
|
||||
Spinner,
|
||||
#[to = "/#switch"]
|
||||
Switch,
|
||||
#[to = "/#tabs"]
|
||||
|
|
|
@ -12,6 +12,7 @@ mod icon;
|
|||
mod input_group;
|
||||
mod menu;
|
||||
mod progressbar;
|
||||
mod spinner;
|
||||
mod switch;
|
||||
mod tabs;
|
||||
mod tag;
|
||||
|
|
45
yewprint-doc/src/spinner/example.rs
Normal file
45
yewprint-doc/src/spinner/example.rs
Normal file
|
@ -0,0 +1,45 @@
|
|||
use yew::prelude::*;
|
||||
use yewprint::{Intent, Spinner};
|
||||
|
||||
pub struct Example {
|
||||
props: ExampleProps,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Properties)]
|
||||
pub struct ExampleProps {
|
||||
pub intent: Option<Intent>,
|
||||
pub size: u32,
|
||||
}
|
||||
|
||||
impl Component for Example {
|
||||
type Message = ();
|
||||
type Properties = ExampleProps;
|
||||
|
||||
fn create(props: Self::Properties, _link: ComponentLink<Self>) -> Self {
|
||||
Example { props }
|
||||
}
|
||||
|
||||
fn update(&mut self, _msg: Self::Message) -> ShouldRender {
|
||||
true
|
||||
}
|
||||
|
||||
fn change(&mut self, props: Self::Properties) -> ShouldRender {
|
||||
if self.props != props {
|
||||
self.props = props;
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn view(&self) -> Html {
|
||||
html! {
|
||||
<div>
|
||||
<Spinner
|
||||
size=self.props.size as f32
|
||||
intent=self.props.intent
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
}
|
101
yewprint-doc/src/spinner/mod.rs
Normal file
101
yewprint-doc/src/spinner/mod.rs
Normal file
|
@ -0,0 +1,101 @@
|
|||
mod example;
|
||||
|
||||
use crate::ExampleContainer;
|
||||
use example::*;
|
||||
use yew::prelude::*;
|
||||
use yewprint::{HtmlSelect, Intent, H1, H5};
|
||||
|
||||
pub struct SpinnerDoc {
|
||||
callback: Callback<ExampleProps>,
|
||||
state: ExampleProps,
|
||||
}
|
||||
|
||||
impl Component for SpinnerDoc {
|
||||
type Message = ExampleProps;
|
||||
type Properties = ();
|
||||
|
||||
fn create(_: Self::Properties, link: ComponentLink<Self>) -> Self {
|
||||
SpinnerDoc {
|
||||
callback: link.callback(|x| x),
|
||||
state: ExampleProps {
|
||||
intent: None,
|
||||
size: 10,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn update(&mut self, msg: Self::Message) -> ShouldRender {
|
||||
self.state = msg;
|
||||
true
|
||||
}
|
||||
|
||||
fn change(&mut self, _props: Self::Properties) -> ShouldRender {
|
||||
true
|
||||
}
|
||||
|
||||
fn view(&self) -> Html {
|
||||
let example_props = self.state.clone();
|
||||
let source = crate::include_raw_html!(
|
||||
concat!(env!("OUT_DIR"), "/", file!(), ".html"),
|
||||
"bp3-code-block"
|
||||
);
|
||||
|
||||
html! {
|
||||
<div>
|
||||
<H1 class=classes!("docs-title")>{"Spinner"}</H1>
|
||||
<div>
|
||||
<ExampleContainer
|
||||
source=source
|
||||
props=Some(html! {
|
||||
<SpinnerProps
|
||||
callback={self.callback.clone()}
|
||||
props=example_props.clone()
|
||||
/>
|
||||
})
|
||||
>
|
||||
<Example with example_props />
|
||||
</ExampleContainer>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
crate::build_example_prop_component! {
|
||||
SpinnerProps for ExampleProps =>
|
||||
fn view(&self) -> Html {
|
||||
html! {
|
||||
<div>
|
||||
<H5>{"Props"}</H5>
|
||||
<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()),
|
||||
]}
|
||||
onchange=self.update_props(|props, intent| ExampleProps {
|
||||
intent,
|
||||
..props
|
||||
})
|
||||
/>
|
||||
<p>{"Select Size:"}</p>
|
||||
<HtmlSelect<u32>
|
||||
options={vec![
|
||||
(20, "Small".to_string()),
|
||||
(50, "Standard".to_string()),
|
||||
(100, "Large".to_string()),
|
||||
]}
|
||||
onchange=self.update_props(|props, size| ExampleProps {
|
||||
size,
|
||||
..props
|
||||
})
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
use crate::icon::SIZE_LARGE;
|
||||
use crate::icon::ICON_SIZE_LARGE;
|
||||
use crate::{Icon, IconName, Intent};
|
||||
use yew::prelude::*;
|
||||
|
||||
|
@ -64,7 +64,7 @@ impl Component for Callout {
|
|||
<div class=classes>
|
||||
{
|
||||
icon.iter()
|
||||
.map(|name| html!{<Icon icon=name icon_size=SIZE_LARGE/>})
|
||||
.map(|name| html!{<Icon icon=name icon_size=ICON_SIZE_LARGE/>})
|
||||
.collect::<Html>()
|
||||
}
|
||||
{
|
||||
|
|
|
@ -9,8 +9,8 @@ impl Default for IconName {
|
|||
}
|
||||
}
|
||||
|
||||
pub const SIZE_STANDARD: i32 = 16;
|
||||
pub const SIZE_LARGE: i32 = 20;
|
||||
pub const ICON_SIZE_STANDARD: i32 = 16;
|
||||
pub const ICON_SIZE_LARGE: i32 = 20;
|
||||
|
||||
pub struct Icon {
|
||||
props: IconProps,
|
||||
|
@ -55,15 +55,15 @@ impl Component for Icon {
|
|||
}
|
||||
|
||||
fn view(&self) -> Html {
|
||||
let paths = if self.props.icon_size == SIZE_STANDARD {
|
||||
let paths = if self.props.icon_size == ICON_SIZE_STANDARD {
|
||||
icon_svg_paths_16(self.props.icon)
|
||||
} else {
|
||||
icon_svg_paths_20(self.props.icon)
|
||||
};
|
||||
let pixel_grid_size = if self.props.icon_size >= SIZE_LARGE {
|
||||
SIZE_LARGE
|
||||
let pixel_grid_size = if self.props.icon_size >= ICON_SIZE_LARGE {
|
||||
ICON_SIZE_LARGE
|
||||
} else {
|
||||
SIZE_STANDARD
|
||||
ICON_SIZE_STANDARD
|
||||
};
|
||||
let icon_string = format!("{:?}", self.props.icon);
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ mod icon;
|
|||
mod input_group;
|
||||
mod menu;
|
||||
mod progressbar;
|
||||
mod spinner;
|
||||
mod switch;
|
||||
mod tabs;
|
||||
mod tag;
|
||||
|
@ -33,6 +34,7 @@ pub use id_tree;
|
|||
pub use input_group::*;
|
||||
pub use menu::*;
|
||||
pub use progressbar::*;
|
||||
pub use spinner::*;
|
||||
pub use switch::*;
|
||||
pub use tabs::*;
|
||||
pub use tag::*;
|
||||
|
|
102
yewprint/src/spinner.rs
Normal file
102
yewprint/src/spinner.rs
Normal file
|
@ -0,0 +1,102 @@
|
|||
use crate::Intent;
|
||||
use yew::prelude::*;
|
||||
|
||||
const R: f32 = 45.0;
|
||||
const PATH_LENGTH: f32 = 280.0;
|
||||
const SPINNER_MIN_SIZE: f32 = 10.0;
|
||||
const STROKE_WIDTH: f32 = 4.0;
|
||||
const MIN_STROKE_WIDTH: f32 = 16.0;
|
||||
pub const SPINNER_SIZE_SMALL: f32 = 20.0;
|
||||
pub const SPINNER_SIZE_STANDARD: f32 = 50.0;
|
||||
pub const SPINNER_SIZE_LARGE: f32 = 100.0;
|
||||
|
||||
pub struct Spinner {
|
||||
props: SpinnerProps,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Properties)]
|
||||
pub struct SpinnerProps {
|
||||
#[prop_or_default]
|
||||
pub intent: Option<Intent>,
|
||||
#[prop_or_default]
|
||||
pub class: Classes,
|
||||
#[prop_or(SPINNER_SIZE_STANDARD)]
|
||||
pub size: f32,
|
||||
#[prop_or(0.25)]
|
||||
pub value: f32,
|
||||
}
|
||||
|
||||
impl Component for Spinner {
|
||||
type Message = ();
|
||||
type Properties = SpinnerProps;
|
||||
|
||||
fn create(props: Self::Properties, _link: ComponentLink<Self>) -> Self {
|
||||
Spinner { props }
|
||||
}
|
||||
|
||||
fn update(&mut self, _msg: Self::Message) -> ShouldRender {
|
||||
true
|
||||
}
|
||||
|
||||
fn change(&mut self, props: Self::Properties) -> ShouldRender {
|
||||
if self.props != props {
|
||||
self.props = props;
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn view(&self) -> Html {
|
||||
let size = f32::max(SPINNER_MIN_SIZE, self.props.size);
|
||||
let stroke_width = f32::min(MIN_STROKE_WIDTH, (STROKE_WIDTH * SPINNER_SIZE_LARGE) / size);
|
||||
let view_box = {
|
||||
let radius = R + stroke_width / 2.00;
|
||||
let view_box_x = 50.00 - radius;
|
||||
let view_box_width = radius * 2.00;
|
||||
format!(
|
||||
"{:.2} {:.2} {:.2} {:.2}",
|
||||
view_box_x, view_box_x, view_box_width, view_box_width,
|
||||
)
|
||||
};
|
||||
let spinner_track = 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 * self.props.value.clamp(0.0, 1.0);
|
||||
|
||||
html! {
|
||||
<div
|
||||
class=classes!(
|
||||
"bp3-spinner",
|
||||
self.props.intent,
|
||||
self.props.class.clone(),
|
||||
)
|
||||
>
|
||||
<div
|
||||
class=classes!("bp3-spinner-animation")
|
||||
>
|
||||
<svg
|
||||
width=size
|
||||
height=size
|
||||
stroke-width=stroke_width
|
||||
viewBox=view_box
|
||||
>
|
||||
<path
|
||||
class=classes!("bp3-spinner-track")
|
||||
d=spinner_track
|
||||
/>
|
||||
<path
|
||||
class=classes!("bp3-spinner-head")
|
||||
d=spinner_track
|
||||
pathLength=PATH_LENGTH
|
||||
stroke-dasharray=format!("{} {}", PATH_LENGTH, PATH_LENGTH)
|
||||
stroke-dashoffset=stroke_offset
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue