Component Icon

This commit is contained in:
Cecile Tonglet 2020-09-13 07:53:12 +02:00
parent 0307fbea11
commit 066dc90444
8 changed files with 229 additions and 11 deletions

32
Cargo.lock generated
View file

@ -32,6 +32,8 @@ dependencies = [
name = "blueprint-rs" name = "blueprint-rs"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"heck",
"regex",
"wasm-bindgen", "wasm-bindgen",
"web-sys", "web-sys",
"yew", "yew",
@ -247,6 +249,15 @@ dependencies = [
"autocfg", "autocfg",
] ]
[[package]]
name = "heck"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205"
dependencies = [
"unicode-segmentation",
]
[[package]] [[package]]
name = "http" name = "http"
version = "0.2.1" version = "0.2.1"
@ -366,6 +377,21 @@ dependencies = [
"proc-macro2", "proc-macro2",
] ]
[[package]]
name = "regex"
version = "1.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c3780fcf44b193bc4d09f36d2a3c87b251da4a046c87795a0d35f4f927ad8e6"
dependencies = [
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.6.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26412eb97c6b088a6997e05f69403a802a92d520de2f8e63c2b65f9e0f47c4e8"
[[package]] [[package]]
name = "ryu" name = "ryu"
version = "1.0.5" version = "1.0.5"
@ -440,6 +466,12 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "unicode-segmentation"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0"
[[package]] [[package]]
name = "unicode-xid" name = "unicode-xid"
version = "0.2.1" version = "0.2.1"

View file

@ -13,3 +13,7 @@ crate-type = ["cdylib"]
wasm-bindgen = "^0.2" wasm-bindgen = "^0.2"
yew = "0.17" yew = "0.17"
web-sys = "0.3" web-sys = "0.3"
[build-dependencies]
regex = { version = "1", default-features = false, features = ["std"] }
heck = "0.3"

58
build.rs Normal file
View file

@ -0,0 +1,58 @@
use heck::CamelCase;
use regex::Regex;
use std::collections::HashSet;
use std::env;
use std::fs;
use std::path::Path;
use std::process::Command;
const ICON_URL: &str = "https://registry.npmjs.org/@blueprintjs/icons/-/icons-3.19.0.tgz";
const ICON_PATH: &str = "package/lib/esnext/generated/iconSvgPaths.js";
fn main() -> Result<(), Box<dyn std::error::Error>> {
let icon_svg_paths = Command::new("sh")
.arg("-c")
.arg(format!(
"curl -sSLo - {} | tar xOzf - {}",
ICON_URL, ICON_PATH
))
.output()
.map(|x| String::from_utf8(x.stdout).expect("source file is not UTF-8"))?;
let out_dir = env::var_os("OUT_DIR").unwrap();
let dest_path = Path::new(&out_dir).join("icon_svg_paths.rs");
let mut src = String::new();
let re_map = Regex::new(r"export const IconSvgPaths([0-9]+) = \{([^}]+)\};").unwrap();
let re_item = Regex::new(r#"(?m)(?s)"([^"]+)": (\[.*?\]),$"#).unwrap();
let mut keys = HashSet::new();
for map in re_map.captures_iter(icon_svg_paths.as_str()) {
src.push_str("fn icon_svg_paths_");
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();
src.push_str("IconName::");
src.push_str(&key);
src.push_str(" => &");
src.push_str(&item[2]);
src.push_str(",\n");
keys.insert(key);
}
src.push_str(" }}\n\n");
}
let mut keys: Vec<_> = keys.iter().collect();
keys.sort();
src.push_str("#[derive(Debug, Copy, Clone, PartialEq)]\npub enum IconName {\n");
for icon in keys {
src.push_str(icon);
src.push_str(",\n");
}
src.push_str("}\n\n");
fs::write(&dest_path, src).unwrap();
println!("cargo:rerun-if-changed=build.rs");
Ok(())
}

View file

@ -10,6 +10,7 @@ if ! [ -f core.tgz ]; then
curl -o core.tgz https://registry.npmjs.org/@blueprintjs/core/-/core-3.30.0.tgz curl -o core.tgz https://registry.npmjs.org/@blueprintjs/core/-/core-3.30.0.tgz
fi fi
mkdir -p static
rm -fR static/.gitignore static/* rm -fR static/.gitignore static/*
tar xvzf core.tgz -C static --wildcards \*.css --transform='s/.*\///' tar xvzf core.tgz -C static --wildcards \*.css --transform='s/.*\///'
wasm-pack build --no-typescript --target web --out-name wasm --out-dir ./static "${options[@]}" "$@" wasm-pack build --no-typescript --target web --out-name wasm --out-dir ./static "${options[@]}" "$@"

View file

@ -1,6 +1,7 @@
use crate::buttons::*; use crate::buttons::Button;
use crate::collapse::*; use crate::collapse::Collapse;
use crate::forms::controls::*; use crate::forms::controls::Switch;
use crate::tree::Tree;
use yew::prelude::*; use yew::prelude::*;
const DARK_BG_COLOR: &str = "#30404d"; const DARK_BG_COLOR: &str = "#30404d";
@ -78,6 +79,7 @@ impl Component for App {
</Button> </Button>
<Collapse <Collapse
is_open=!self.collapsed is_open=!self.collapsed
keep_children_mounted=true
> >
<pre class="bp3-code-block"> <pre class="bp3-code-block">
<div>{"[INFO]: Installing wasm-bindgen..."}</div> <div>{"[INFO]: Installing wasm-bindgen..."}</div>
@ -98,6 +100,9 @@ impl Component for App {
</pre> </pre>
</Collapse> </Collapse>
</div> </div>
<div>
<Tree />
</div>
</div> </div>
} }
} }

90
src/icon.rs Normal file
View file

@ -0,0 +1,90 @@
use yew::prelude::*;
include!(concat!(env!("OUT_DIR"), "/icon_svg_paths.rs"));
pub const SIZE_STANDARD: i32 = 16;
pub const SIZE_LARGE: i32 = 20;
pub struct Icon {
props: Props,
}
#[derive(Clone, PartialEq, Properties)]
pub struct Props {
pub icon: IconName,
#[prop_or_default]
pub class: String,
#[prop_or_default]
pub title: Option<String>,
#[prop_or_default]
pub color: Option<String>,
#[prop_or(16)]
pub icon_size: i32,
#[prop_or_default]
pub onclick: Callback<MouseEvent>,
}
impl Component for Icon {
type Message = ();
type Properties = Props;
fn create(props: Self::Properties, _link: ComponentLink<Self>) -> Self {
Icon { props }
}
fn update(&mut self, _msg: Self::Message) -> ShouldRender {
true
}
fn change(&mut self, props: Self::Properties) -> ShouldRender {
if self.props != props {
self.props = props;
true
} else {
false
}
}
fn view(&self) -> Html {
let mut class = "bp3-icon ".to_string();
class.push_str(self.props.class.as_str());
let paths = if self.props.icon_size == 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
} else {
SIZE_STANDARD
};
html! {
<span class="bp3-icon" onclick={self.props.onclick.clone()}>
<svg
fill={self.props.color.clone().unwrap_or_default()}
data-icon={format!("{:?}", self.props.icon)}
width={self.props.icon_size}
height={self.props.icon_size}
viewBox={format!("0 0 {x} {x}", x=pixel_grid_size)}
>
{
if let Some(title) = self.props.title.clone() {
html!(<desc>{title}</desc>)
} else {
Default::default()
}
}
{
paths.iter()
.map(|x| html! {
<path d=x fillRule="evenodd" />
})
.collect::<Vec<_>>()
}
</svg>
</span>
}
}
}

View file

@ -1,10 +1,11 @@
#![recursion_limit = "512"] #![recursion_limit = "512"]
mod app; mod app;
mod buttons; pub mod buttons;
mod collapse; pub mod collapse;
mod forms; pub mod forms;
mod tree; pub mod icon;
pub mod tree;
use wasm_bindgen::prelude::*; use wasm_bindgen::prelude::*;

View file

@ -1,3 +1,5 @@
use crate::collapse::Collapse;
use crate::icon::{Icon, IconName};
use yew::prelude::*; use yew::prelude::*;
pub struct Tree { pub struct Tree {
@ -5,7 +7,10 @@ pub struct Tree {
} }
#[derive(Clone, PartialEq, Properties)] #[derive(Clone, PartialEq, Properties)]
pub struct Props {} pub struct Props {
#[prop_or_default]
is_expanded: bool,
}
impl Component for Tree { impl Component for Tree {
type Message = (); type Message = ();
@ -32,7 +37,9 @@ impl Component for Tree {
html! { html! {
<div class="bp3-tree"> <div class="bp3-tree">
<ul class="bp3-tree-node-list"> <ul class="bp3-tree-node-list">
<TreeNode/> <TreeNode is_expanded=self.props.is_expanded has_caret=true>
{"Example Stuff"}
</TreeNode>
</ul> </ul>
</div> </div>
} }
@ -44,7 +51,14 @@ pub struct TreeNode {
} }
#[derive(Clone, PartialEq, Properties)] #[derive(Clone, PartialEq, Properties)]
pub struct TreeNodeProps {} pub struct TreeNodeProps {
#[prop_or_default]
pub is_expanded: bool,
#[prop_or_default]
pub has_caret: bool,
#[prop_or_default]
pub children: html::Children,
}
impl Component for TreeNode { impl Component for TreeNode {
type Message = (); type Message = ();
@ -71,9 +85,22 @@ impl Component for TreeNode {
html! { html! {
<li class="bp3-tree-node"> <li class="bp3-tree-node">
<div class="bp3-tree-node-content"> <div class="bp3-tree-node-content">
{
if self.props.has_caret {
html! {
<Icon class="bp3-tree-node-caret" icon=IconName::ChevronRight />
}
} else {
html! {
<span class="bp3-tree-node-caret-none" />
}
}
}
{"content"} {"content"}
</div> </div>
// missing <Collapse/> <Collapse is_open=self.props.is_expanded>
{self.props.children.clone()}
</Collapse>
</li> </li>
} }
} }