Initial commit
This commit is contained in:
commit
96b6152d7b
20 changed files with 914 additions and 0 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
target
|
||||||
|
Cargo.lock
|
20
Cargo.toml
Normal file
20
Cargo.toml
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
[package]
|
||||||
|
name = "bootstrap-rs"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Thomas Gideon <cmdln@thecommandline.net>"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
crate-type = ["cdylib", "rlib"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
yew = "~0.16.2"
|
||||||
|
log = "~0.4.8"
|
||||||
|
serde_json = "^1.0.40"
|
||||||
|
serde = "^1.0.98"
|
||||||
|
anyhow = "^1.0.28"
|
||||||
|
wasm-bindgen = "~0.2.61"
|
||||||
|
web-sys = "~0.3.38"
|
||||||
|
wasm-logger = "~0.2.0"
|
||||||
|
yew-router = "~0.13.0"
|
||||||
|
yew-components = "~0.1.2"
|
46
src/breadcrumb/item.rs
Normal file
46
src/breadcrumb/item.rs
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
use crate::prelude::*;
|
||||||
|
use yew::{html::Children, prelude::*};
|
||||||
|
|
||||||
|
pub struct BreadcrumbItem {
|
||||||
|
props: Props,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Properties, Clone, PartialEq)]
|
||||||
|
pub struct Props {
|
||||||
|
pub active: bool,
|
||||||
|
#[prop_or_default]
|
||||||
|
pub children: Children,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Component for BreadcrumbItem {
|
||||||
|
type Properties = Props;
|
||||||
|
type Message = ();
|
||||||
|
|
||||||
|
fn create(props: Self::Properties, _: ComponentLink<Self>) -> Self {
|
||||||
|
Self { props }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update(&mut self, _: Self::Message) -> ShouldRender {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn change(&mut self, props: Self::Properties) -> ShouldRender {
|
||||||
|
render_on_change(&mut self.props, props)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn view(&self) -> Html {
|
||||||
|
if self.props.active {
|
||||||
|
html! {
|
||||||
|
<li class="breadcrumb-item active" aria-current="page">
|
||||||
|
{ self.props.children.render() }
|
||||||
|
</li>
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
html! {
|
||||||
|
<li class="breadcrumb-item">
|
||||||
|
{ self.props.children.render() }
|
||||||
|
</li>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
77
src/breadcrumb/mod.rs
Normal file
77
src/breadcrumb/mod.rs
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
mod item;
|
||||||
|
|
||||||
|
use crate::prelude::*;
|
||||||
|
pub use item::BreadcrumbItem;
|
||||||
|
use yew::{html::Children, prelude::*};
|
||||||
|
|
||||||
|
pub struct Breadcrumb {
|
||||||
|
props: Props,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Properties, Clone, PartialEq)]
|
||||||
|
pub struct Props {
|
||||||
|
#[prop_or_default]
|
||||||
|
pub border: Option<Border>,
|
||||||
|
#[prop_or_default]
|
||||||
|
pub borders: Vec<Border>,
|
||||||
|
#[prop_or_default]
|
||||||
|
pub border_color: Option<BorderColor>,
|
||||||
|
#[prop_or_default]
|
||||||
|
pub border_colors: Vec<BorderColor>,
|
||||||
|
#[prop_or_default]
|
||||||
|
pub margin: Option<Margin>,
|
||||||
|
#[prop_or_default]
|
||||||
|
pub margins: Vec<Margin>,
|
||||||
|
#[prop_or_default]
|
||||||
|
pub class: String,
|
||||||
|
#[prop_or_default]
|
||||||
|
pub style: String,
|
||||||
|
#[prop_or_default]
|
||||||
|
pub children: Children,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Component for Breadcrumb {
|
||||||
|
type Properties = Props;
|
||||||
|
type Message = ();
|
||||||
|
|
||||||
|
fn create(props: Self::Properties, _: ComponentLink<Self>) -> Self {
|
||||||
|
Self { props }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update(&mut self, _: Self::Message) -> ShouldRender {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn change(&mut self, props: Self::Properties) -> ShouldRender {
|
||||||
|
render_on_change(&mut self.props, props)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn view(&self) -> Html {
|
||||||
|
html! {
|
||||||
|
<nav
|
||||||
|
class=self.props.class.clone()
|
||||||
|
aria-label="breadcrumb"
|
||||||
|
style=self.props.style.clone()
|
||||||
|
>
|
||||||
|
<ol
|
||||||
|
class="breadcrumb"
|
||||||
|
>
|
||||||
|
{ self.props.children.render() }
|
||||||
|
</ol>
|
||||||
|
</nav>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<&'a Props> for BootstrapProps<'a> {
|
||||||
|
fn from(props: &Props) -> BootstrapProps {
|
||||||
|
let borders = collect_bs(&props.border, &props.borders);
|
||||||
|
let border_colors = collect_bs(&props.border_color, &props.border_colors);
|
||||||
|
let margins = collect_bs(&props.margin, &props.margins);
|
||||||
|
BootstrapProps {
|
||||||
|
borders,
|
||||||
|
border_colors,
|
||||||
|
margins,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
67
src/button/mod.rs
Normal file
67
src/button/mod.rs
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
use crate::prelude::*;
|
||||||
|
use yew::{html::Children, prelude::*};
|
||||||
|
|
||||||
|
pub struct ButtonGroup {
|
||||||
|
props: Props,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Properties, Clone, PartialEq)]
|
||||||
|
pub struct Props {
|
||||||
|
#[prop_or_default]
|
||||||
|
pub class: String,
|
||||||
|
#[prop_or_default]
|
||||||
|
pub style: String,
|
||||||
|
#[prop_or_default]
|
||||||
|
pub role: String,
|
||||||
|
#[prop_or_default]
|
||||||
|
pub aria_label: String,
|
||||||
|
#[prop_or_default]
|
||||||
|
pub children: Children,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Component for ButtonGroup {
|
||||||
|
type Properties = Props;
|
||||||
|
type Message = ();
|
||||||
|
|
||||||
|
fn create(props: Self::Properties, _: ComponentLink<Self>) -> Self {
|
||||||
|
Self { props }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update(&mut self, _: Self::Message) -> ShouldRender {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn change(&mut self, props: Self::Properties) -> ShouldRender {
|
||||||
|
render_on_change(&mut self.props, props)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn view(&self) -> Html {
|
||||||
|
html! {
|
||||||
|
<div class=self.class()
|
||||||
|
style=self.style()
|
||||||
|
role=self.props.role.clone()
|
||||||
|
aria-label=self.props.aria_label.clone()
|
||||||
|
>
|
||||||
|
{ self.props.children.render() }
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ButtonGroup {
|
||||||
|
fn class(&self) -> String {
|
||||||
|
if self.props.class.is_empty() {
|
||||||
|
"btn-grp".into()
|
||||||
|
} else {
|
||||||
|
format!("btn-group {}", self.props.class)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn style(&self) -> &str {
|
||||||
|
if self.props.style.is_empty() {
|
||||||
|
""
|
||||||
|
} else {
|
||||||
|
&self.props.style
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
59
src/card/body.rs
Normal file
59
src/card/body.rs
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
use crate::prelude::*;
|
||||||
|
use yew::{html::Children, prelude::*};
|
||||||
|
|
||||||
|
pub struct CardBody {
|
||||||
|
props: Props,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Properties, Clone, PartialEq)]
|
||||||
|
pub struct Props {
|
||||||
|
#[prop_or_default]
|
||||||
|
pub class: String,
|
||||||
|
#[prop_or_default]
|
||||||
|
pub style: String,
|
||||||
|
#[prop_or_default]
|
||||||
|
pub children: Children,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Component for CardBody {
|
||||||
|
type Properties = Props;
|
||||||
|
type Message = ();
|
||||||
|
|
||||||
|
fn create(props: Self::Properties, _: ComponentLink<Self>) -> Self {
|
||||||
|
Self { props }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update(&mut self, _: Self::Message) -> ShouldRender {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn change(&mut self, props: Self::Properties) -> ShouldRender {
|
||||||
|
render_on_change(&mut self.props, props)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn view(&self) -> Html {
|
||||||
|
html! {
|
||||||
|
<div class=self.class() style=self.style()>
|
||||||
|
{ self.props.children.render() }
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CardBody {
|
||||||
|
fn class(&self) -> String {
|
||||||
|
if self.props.class.is_empty() {
|
||||||
|
"card-body".into()
|
||||||
|
} else {
|
||||||
|
format!("card-body {}", self.props.class)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn style(&self) -> &str {
|
||||||
|
if self.props.style.is_empty() {
|
||||||
|
""
|
||||||
|
} else {
|
||||||
|
&self.props.style
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
59
src/card/header.rs
Normal file
59
src/card/header.rs
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
use crate::prelude::*;
|
||||||
|
use yew::{html::Children, prelude::*};
|
||||||
|
|
||||||
|
pub struct CardHeader {
|
||||||
|
props: Props,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Properties, Clone, PartialEq)]
|
||||||
|
pub struct Props {
|
||||||
|
#[prop_or_default]
|
||||||
|
pub class: String,
|
||||||
|
#[prop_or_default]
|
||||||
|
pub style: String,
|
||||||
|
#[prop_or_default]
|
||||||
|
pub children: Children,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Component for CardHeader {
|
||||||
|
type Properties = Props;
|
||||||
|
type Message = ();
|
||||||
|
|
||||||
|
fn create(props: Self::Properties, _: ComponentLink<Self>) -> Self {
|
||||||
|
Self { props }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update(&mut self, _: Self::Message) -> ShouldRender {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn change(&mut self, props: Self::Properties) -> ShouldRender {
|
||||||
|
render_on_change(&mut self.props, props)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn view(&self) -> Html {
|
||||||
|
html! {
|
||||||
|
<p class=self.class() style=self.style()>
|
||||||
|
{ self.props.children.render() }
|
||||||
|
</p>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CardHeader {
|
||||||
|
fn class(&self) -> String {
|
||||||
|
if self.props.class.is_empty() {
|
||||||
|
"card-header".into()
|
||||||
|
} else {
|
||||||
|
format!("card-header {}", self.props.class)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn style(&self) -> &str {
|
||||||
|
if self.props.style.is_empty() {
|
||||||
|
""
|
||||||
|
} else {
|
||||||
|
&self.props.style
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
93
src/card/mod.rs
Normal file
93
src/card/mod.rs
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
mod body;
|
||||||
|
mod header;
|
||||||
|
mod text;
|
||||||
|
|
||||||
|
pub use self::{body::CardBody, header::CardHeader, text::CardText};
|
||||||
|
use crate::prelude::*;
|
||||||
|
use yew::{html::Children, prelude::*};
|
||||||
|
|
||||||
|
pub struct Card {
|
||||||
|
props: Props,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Properties, Clone, PartialEq)]
|
||||||
|
pub struct Props {
|
||||||
|
#[prop_or_default]
|
||||||
|
pub margin: Option<Margin>,
|
||||||
|
#[prop_or_default]
|
||||||
|
pub margins: Vec<Margin>,
|
||||||
|
#[prop_or_default]
|
||||||
|
pub border: Option<Border>,
|
||||||
|
#[prop_or_default]
|
||||||
|
pub borders: Vec<Border>,
|
||||||
|
#[prop_or_default]
|
||||||
|
pub border_color: Option<BorderColor>,
|
||||||
|
#[prop_or_default]
|
||||||
|
pub border_colors: Vec<BorderColor>,
|
||||||
|
#[prop_or_default]
|
||||||
|
pub class: String,
|
||||||
|
#[prop_or_default]
|
||||||
|
pub style: String,
|
||||||
|
#[prop_or_default]
|
||||||
|
pub children: Children,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Component for Card {
|
||||||
|
type Properties = Props;
|
||||||
|
type Message = ();
|
||||||
|
|
||||||
|
fn create(props: Self::Properties, _: ComponentLink<Self>) -> Self {
|
||||||
|
Self { props }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update(&mut self, _: Self::Message) -> ShouldRender {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn change(&mut self, props: Self::Properties) -> ShouldRender {
|
||||||
|
render_on_change(&mut self.props, props)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn view(&self) -> Html {
|
||||||
|
html! {
|
||||||
|
<div class=self.class() style=self.style()>
|
||||||
|
{ self.props.children.render() }
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Card {
|
||||||
|
fn class(&self) -> String {
|
||||||
|
if self.props.class.is_empty() {
|
||||||
|
format!("card {}", calculate_classes((&self.props).into()))
|
||||||
|
} else {
|
||||||
|
format!(
|
||||||
|
"card {} {}",
|
||||||
|
self.props.class,
|
||||||
|
calculate_classes((&self.props).into())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn style(&self) -> &str {
|
||||||
|
if self.props.style.is_empty() {
|
||||||
|
""
|
||||||
|
} else {
|
||||||
|
&self.props.style
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<&'a Props> for BootstrapProps<'a> {
|
||||||
|
fn from(props: &'a Props) -> Self {
|
||||||
|
let borders = collect_bs(&props.border, &props.borders);
|
||||||
|
let border_colors = collect_bs(&props.border_color, &props.border_colors);
|
||||||
|
let margins = collect_bs(&props.margin, &props.margins);
|
||||||
|
Self {
|
||||||
|
borders,
|
||||||
|
border_colors,
|
||||||
|
margins,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
59
src/card/text.rs
Normal file
59
src/card/text.rs
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
use crate::prelude::*;
|
||||||
|
use yew::{html::Children, prelude::*};
|
||||||
|
|
||||||
|
pub struct CardText {
|
||||||
|
props: Props,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Properties, Clone, PartialEq)]
|
||||||
|
pub struct Props {
|
||||||
|
#[prop_or_default]
|
||||||
|
pub class: String,
|
||||||
|
#[prop_or_default]
|
||||||
|
pub style: String,
|
||||||
|
#[prop_or_default]
|
||||||
|
pub children: Children,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Component for CardText {
|
||||||
|
type Properties = Props;
|
||||||
|
type Message = ();
|
||||||
|
|
||||||
|
fn create(props: Self::Properties, _: ComponentLink<Self>) -> Self {
|
||||||
|
Self { props }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update(&mut self, _: Self::Message) -> ShouldRender {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn change(&mut self, props: Self::Properties) -> ShouldRender {
|
||||||
|
render_on_change(&mut self.props, props)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn view(&self) -> Html {
|
||||||
|
html! {
|
||||||
|
<p class=self.class() style=self.style()>
|
||||||
|
{ self.props.children.render() }
|
||||||
|
</p>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CardText {
|
||||||
|
fn class(&self) -> String {
|
||||||
|
if self.props.class.is_empty() {
|
||||||
|
"card-text".into()
|
||||||
|
} else {
|
||||||
|
format!("card-text {}", self.props.class)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn style(&self) -> &str {
|
||||||
|
if self.props.style.is_empty() {
|
||||||
|
""
|
||||||
|
} else {
|
||||||
|
&self.props.style
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
37
src/container.rs
Normal file
37
src/container.rs
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
use crate::prelude::*;
|
||||||
|
use yew::{html::Children, prelude::*};
|
||||||
|
|
||||||
|
pub struct Container {
|
||||||
|
props: Props,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Properties, Clone, PartialEq)]
|
||||||
|
pub struct Props {
|
||||||
|
#[prop_or_default]
|
||||||
|
pub children: Children,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Component for Container {
|
||||||
|
type Properties = Props;
|
||||||
|
type Message = ();
|
||||||
|
|
||||||
|
fn create(props: Self::Properties, _: ComponentLink<Self>) -> Self {
|
||||||
|
Self { props }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update(&mut self, _: Self::Message) -> ShouldRender {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn change(&mut self, props: Self::Properties) -> ShouldRender {
|
||||||
|
render_on_change(&mut self.props, props)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn view(&self) -> Html {
|
||||||
|
html! {
|
||||||
|
<div class="container">
|
||||||
|
{ self.props.children.render() }
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
57
src/input/group.rs
Normal file
57
src/input/group.rs
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
use crate::prelude::*;
|
||||||
|
use yew::{html::Children, prelude::*};
|
||||||
|
|
||||||
|
pub struct InputGroup {
|
||||||
|
props: Props,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Properties, Clone, PartialEq)]
|
||||||
|
pub struct Props {
|
||||||
|
#[prop_or_default]
|
||||||
|
pub margin: Option<Margin>,
|
||||||
|
#[prop_or_default]
|
||||||
|
pub margins: Vec<Margin>,
|
||||||
|
#[prop_or_default]
|
||||||
|
pub class: String,
|
||||||
|
#[prop_or_default]
|
||||||
|
pub children: Children,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<&'a Props> for BootstrapProps<'a> {
|
||||||
|
fn from(props: &'a Props) -> BootstrapProps<'a> {
|
||||||
|
let borders = Vec::new();
|
||||||
|
let border_colors = Vec::new();
|
||||||
|
let margins = collect_bs(&props.margin, &props.margins);
|
||||||
|
Self {
|
||||||
|
borders,
|
||||||
|
border_colors,
|
||||||
|
margins,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Component for InputGroup {
|
||||||
|
type Message = ();
|
||||||
|
type Properties = Props;
|
||||||
|
|
||||||
|
fn create(props: Self::Properties, _: ComponentLink<Self>) -> Self {
|
||||||
|
Self { props }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update(&mut self, _: Self::Message) -> ShouldRender {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn change(&mut self, props: Self::Properties) -> ShouldRender {
|
||||||
|
render_on_change(&mut self.props, props)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn view(&self) -> Html {
|
||||||
|
let classes = calculate_classes((&self.props).into());
|
||||||
|
html! {
|
||||||
|
<div class=format!("input-group {} {}", classes, self.props.class)>
|
||||||
|
{ self.props.children.render() }
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
88
src/input/mod.rs
Normal file
88
src/input/mod.rs
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
mod group;
|
||||||
|
mod textarea;
|
||||||
|
|
||||||
|
pub use self::{group::InputGroup, textarea::TextArea};
|
||||||
|
use crate::prelude::*;
|
||||||
|
use yew::prelude::*;
|
||||||
|
|
||||||
|
#[derive(Clone, PartialEq)]
|
||||||
|
pub enum InputType {
|
||||||
|
Text,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InputType {
|
||||||
|
fn as_str(&self) -> &str {
|
||||||
|
match self {
|
||||||
|
Self::Text => "text",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Input {
|
||||||
|
link: ComponentLink<Self>,
|
||||||
|
state: String,
|
||||||
|
props: Props,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct InputChange(ChangeData);
|
||||||
|
|
||||||
|
#[derive(Properties, Clone, PartialEq)]
|
||||||
|
pub struct Props {
|
||||||
|
#[prop_or_default]
|
||||||
|
pub name: String,
|
||||||
|
#[prop_or_default]
|
||||||
|
pub id: String,
|
||||||
|
pub on_signal: Callback<String>,
|
||||||
|
pub input_type: InputType,
|
||||||
|
#[prop_or_default]
|
||||||
|
pub readonly: bool,
|
||||||
|
#[prop_or_default]
|
||||||
|
pub value: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Component for Input {
|
||||||
|
type Message = InputChange;
|
||||||
|
type Properties = Props;
|
||||||
|
|
||||||
|
fn create(props: Self::Properties, link: ComponentLink<Self>) -> Self {
|
||||||
|
let state = props.value.clone();
|
||||||
|
Self { props, state, link }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update(&mut self, msg: Self::Message) -> ShouldRender {
|
||||||
|
if let InputChange(ChangeData::Value(value)) = msg {
|
||||||
|
self.state = value.clone();
|
||||||
|
self.props.on_signal.emit(value);
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn change(&mut self, props: Self::Properties) -> ShouldRender {
|
||||||
|
if self.props.value != props.value {
|
||||||
|
self.state = props.value.clone();
|
||||||
|
}
|
||||||
|
render_on_change(&mut self.props, props)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn view(&self) -> Html {
|
||||||
|
let class = if self.props.readonly {
|
||||||
|
"form-control-plaintext pl-3"
|
||||||
|
} else {
|
||||||
|
"form-control"
|
||||||
|
};
|
||||||
|
html! {
|
||||||
|
<input
|
||||||
|
name=&self.props.name
|
||||||
|
id=&self.props.id
|
||||||
|
type=self.props.input_type.as_str()
|
||||||
|
class=class
|
||||||
|
value=&self.state
|
||||||
|
readonly=self.props.readonly
|
||||||
|
onchange=self.link.callback(|evt| InputChange(evt))
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
57
src/input/textarea.rs
Normal file
57
src/input/textarea.rs
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
use crate::prelude::*;
|
||||||
|
use yew::prelude::*;
|
||||||
|
|
||||||
|
pub struct TextArea {
|
||||||
|
link: ComponentLink<Self>,
|
||||||
|
state: String,
|
||||||
|
props: Props,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct InputChange(ChangeData);
|
||||||
|
|
||||||
|
#[derive(Properties, Clone, PartialEq)]
|
||||||
|
pub struct Props {
|
||||||
|
#[prop_or_default]
|
||||||
|
pub name: String,
|
||||||
|
#[prop_or_default]
|
||||||
|
pub id: String,
|
||||||
|
pub on_signal: Callback<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Component for TextArea {
|
||||||
|
type Message = InputChange;
|
||||||
|
type Properties = Props;
|
||||||
|
|
||||||
|
fn create(props: Self::Properties, link: ComponentLink<Self>) -> Self {
|
||||||
|
let state = String::default();
|
||||||
|
Self { props, state, link }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update(&mut self, msg: Self::Message) -> ShouldRender {
|
||||||
|
if let InputChange(ChangeData::Value(value)) = msg {
|
||||||
|
self.state = value.clone();
|
||||||
|
self.props.on_signal.emit(value);
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn change(&mut self, props: Self::Properties) -> ShouldRender {
|
||||||
|
render_on_change(&mut self.props, props)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn view(&self) -> Html {
|
||||||
|
html! {
|
||||||
|
<textarea
|
||||||
|
name=&self.props.name
|
||||||
|
id=&self.props.id
|
||||||
|
class="form-control"
|
||||||
|
onchange=self.link.callback(|evt| InputChange(evt))
|
||||||
|
>
|
||||||
|
{ &self.state }
|
||||||
|
</textarea>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
43
src/jumbotron.rs
Normal file
43
src/jumbotron.rs
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
use crate::prelude::*;
|
||||||
|
use yew::{html::Children, prelude::*};
|
||||||
|
|
||||||
|
pub struct Jumbotron {
|
||||||
|
props: Props,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Properties, Clone)]
|
||||||
|
pub struct Props {
|
||||||
|
#[prop_or_default]
|
||||||
|
pub margin: Option<Margin>,
|
||||||
|
#[prop_or_default]
|
||||||
|
pub margins: Vec<Margin>,
|
||||||
|
#[prop_or_default]
|
||||||
|
pub class: String,
|
||||||
|
#[prop_or_default]
|
||||||
|
pub children: Children,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Component for Jumbotron {
|
||||||
|
type Properties = Props;
|
||||||
|
type Message = ();
|
||||||
|
|
||||||
|
fn create(props: Self::Properties, _: ComponentLink<Self>) -> Self {
|
||||||
|
Self { props }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update(&mut self, _: Self::Message) -> ShouldRender {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn change(&mut self, _: Self::Properties) -> ShouldRender {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn view(&self) -> Html {
|
||||||
|
html! {
|
||||||
|
<div class=format!("jumbotron {}", self.props.class)>
|
||||||
|
{ self.props.children.render() }
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
16
src/lib.rs
Normal file
16
src/lib.rs
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
mod breadcrumb;
|
||||||
|
mod button;
|
||||||
|
mod card;
|
||||||
|
mod container;
|
||||||
|
pub mod input;
|
||||||
|
mod jumbotron;
|
||||||
|
pub mod prelude;
|
||||||
|
|
||||||
|
pub use self::{
|
||||||
|
breadcrumb::{Breadcrumb, BreadcrumbItem},
|
||||||
|
button::ButtonGroup,
|
||||||
|
card::{Card, CardBody, CardHeader, CardText},
|
||||||
|
container::Container,
|
||||||
|
input::{Input, InputGroup, TextArea},
|
||||||
|
jumbotron::Jumbotron,
|
||||||
|
};
|
20
src/prelude/border.rs
Normal file
20
src/prelude/border.rs
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
#[derive(Clone, PartialEq)]
|
||||||
|
pub enum Border {
|
||||||
|
All,
|
||||||
|
Top,
|
||||||
|
Right,
|
||||||
|
Bottom,
|
||||||
|
Left,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl super::BootstrapClass for Border {
|
||||||
|
fn as_classname(&self) -> String {
|
||||||
|
match self {
|
||||||
|
Self::All => "border".into(),
|
||||||
|
Self::Top => "border-top".into(),
|
||||||
|
Self::Right => "border-right".into(),
|
||||||
|
Self::Bottom => "border-bottom".into(),
|
||||||
|
Self::Left => "border-left".into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
22
src/prelude/border_color.rs
Normal file
22
src/prelude/border_color.rs
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
#[derive(Clone, PartialEq)]
|
||||||
|
pub enum BorderColor {
|
||||||
|
Primary,
|
||||||
|
Secondary,
|
||||||
|
Unset,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for BorderColor {
|
||||||
|
fn default() -> Self {
|
||||||
|
BorderColor::Unset
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl super::BootstrapClass for BorderColor {
|
||||||
|
fn as_classname(&self) -> String {
|
||||||
|
match self {
|
||||||
|
Self::Primary => "border-primary".into(),
|
||||||
|
Self::Secondary => "border-secondary".into(),
|
||||||
|
Self::Unset => "".into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
8
src/prelude/margin.rs
Normal file
8
src/prelude/margin.rs
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub struct Margin(pub super::Edge, pub usize);
|
||||||
|
|
||||||
|
impl super::BootstrapClass for Margin {
|
||||||
|
fn as_classname(&self) -> String {
|
||||||
|
format!("m{}-{}", self.0, self.1)
|
||||||
|
}
|
||||||
|
}
|
76
src/prelude/mod.rs
Normal file
76
src/prelude/mod.rs
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
mod border;
|
||||||
|
mod border_color;
|
||||||
|
mod margin;
|
||||||
|
|
||||||
|
pub use self::{border::Border, border_color::BorderColor, margin::Margin};
|
||||||
|
use std::fmt::{Display, Formatter, Result};
|
||||||
|
use yew::prelude::*;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub enum Edge {
|
||||||
|
Top,
|
||||||
|
Right,
|
||||||
|
Bottom,
|
||||||
|
Left,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Edge {
|
||||||
|
fn fmt(&self, fmt: &mut Formatter) -> Result {
|
||||||
|
match self {
|
||||||
|
Self::Top => write!(fmt, "t"),
|
||||||
|
Self::Bottom => write!(fmt, "b"),
|
||||||
|
Self::Right => write!(fmt, "r"),
|
||||||
|
Self::Left => write!(fmt, "l"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
trait BootstrapClass {
|
||||||
|
fn as_classname(&self) -> String;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct BootstrapProps<'a> {
|
||||||
|
pub borders: Vec<&'a Border>,
|
||||||
|
pub border_colors: Vec<&'a BorderColor>,
|
||||||
|
pub margins: Vec<&'a Margin>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render_on_change<P: Properties + PartialEq>(
|
||||||
|
props_on_comp: &mut P,
|
||||||
|
props: P,
|
||||||
|
) -> ShouldRender {
|
||||||
|
if props_on_comp == &props {
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
*props_on_comp = props;
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn collect_bs<'a, T>(t: &'a Option<T>, ts: &'a [T]) -> Vec<&'a T> {
|
||||||
|
if let Some(t) = t.as_ref() {
|
||||||
|
let mut r = vec![t];
|
||||||
|
r.append(&mut ts.iter().collect());
|
||||||
|
r
|
||||||
|
} else {
|
||||||
|
ts.iter().collect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn calculate_classes(props: BootstrapProps) -> String {
|
||||||
|
let BootstrapProps {
|
||||||
|
borders,
|
||||||
|
border_colors,
|
||||||
|
margins,
|
||||||
|
} = props;
|
||||||
|
let mut classes = Vec::new();
|
||||||
|
classes.append(&mut into_classnames(borders));
|
||||||
|
classes.append(&mut into_classnames(border_colors));
|
||||||
|
classes.append(&mut into_classnames(margins));
|
||||||
|
|
||||||
|
classes.join(" ")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_classnames<C: BootstrapClass>(c: Vec<&C>) -> Vec<String> {
|
||||||
|
c.into_iter().map(|c| c.as_classname()).collect()
|
||||||
|
}
|
8
src/prelude/padding.rs
Normal file
8
src/prelude/padding.rs
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub struct Padding(pub super::Edge, pub usize);
|
||||||
|
|
||||||
|
impl super::BootstrapClass for Padding {
|
||||||
|
fn as_classname(&self) -> String {
|
||||||
|
format!("m{}-{}", self.0, self.1)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue