mirror of
https://github.com/yewprint/yewprint
synced 2025-02-16 12:08:25 +00:00
WIP: Tree
This commit is contained in:
parent
066dc90444
commit
584f5f69b1
6 changed files with 207 additions and 34 deletions
25
Cargo.lock
generated
25
Cargo.lock
generated
|
@ -33,6 +33,7 @@ name = "blueprint-rs"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"id_tree",
|
||||
"regex",
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
|
@ -269,6 +270,15 @@ dependencies = [
|
|||
"itoa",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "id_tree"
|
||||
version = "1.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8233bef841ffcb4766af63c917b09c786430cfcfbaf41bffeae57d53284ddd00"
|
||||
dependencies = [
|
||||
"snowflake",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "1.5.2"
|
||||
|
@ -435,6 +445,12 @@ version = "0.4.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8"
|
||||
|
||||
[[package]]
|
||||
name = "snowflake"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "27207bb65232eda1f588cf46db2fee75c0808d557f6b3cf19a75f5d6d7c94df1"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.39"
|
||||
|
@ -557,8 +573,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "yew"
|
||||
version = "0.17.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "882b275d108d2280dbc588a8ad6c9690d806fcd299d63b68d702ff4d848fdc0c"
|
||||
source = "git+https://github.com/yewstack/yew.git?rev=1507c21b#1507c21b39e7941f7b9fe8ff5af792cf55be1f6f"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"anymap",
|
||||
|
@ -572,8 +587,6 @@ dependencies = [
|
|||
"indexmap",
|
||||
"js-sys",
|
||||
"log",
|
||||
"proc-macro-hack",
|
||||
"proc-macro-nested",
|
||||
"ryu",
|
||||
"serde",
|
||||
"serde_json",
|
||||
|
@ -588,12 +601,10 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "yew-macro"
|
||||
version = "0.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "61a9a452e63b6222b28b426dafbc6b207192e0127cdb93324cc7407b8c7e1768"
|
||||
source = "git+https://github.com/yewstack/yew.git?rev=1507c21b#1507c21b39e7941f7b9fe8ff5af792cf55be1f6f"
|
||||
dependencies = [
|
||||
"boolinator",
|
||||
"lazy_static",
|
||||
"proc-macro-hack",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
|
|
|
@ -11,8 +11,9 @@ crate-type = ["cdylib"]
|
|||
|
||||
[dependencies]
|
||||
wasm-bindgen = "^0.2"
|
||||
yew = "0.17"
|
||||
yew = { git = "https://github.com/yewstack/yew.git", rev = "1507c21b" }
|
||||
web-sys = "0.3"
|
||||
id_tree = "1.7"
|
||||
|
||||
[build-dependencies]
|
||||
regex = { version = "1", default-features = false, features = ["std"] }
|
||||
|
|
|
@ -12,6 +12,10 @@
|
|||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.bp3-tree {
|
||||
width: 350px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body></body>
|
||||
|
|
82
src/app.rs
82
src/app.rs
|
@ -1,7 +1,8 @@
|
|||
use crate::buttons::Button;
|
||||
use crate::collapse::Collapse;
|
||||
use crate::forms::controls::Switch;
|
||||
use crate::tree::Tree;
|
||||
use crate::icon::IconName;
|
||||
use crate::tree::{NodeData, Tree};
|
||||
use yew::prelude::*;
|
||||
|
||||
const DARK_BG_COLOR: &str = "#30404d";
|
||||
|
@ -14,12 +15,15 @@ pub struct App {
|
|||
counter: i64,
|
||||
dark_theme: bool,
|
||||
collapsed: bool,
|
||||
tree: id_tree::Tree<NodeData<i32>>,
|
||||
node_dir1_id: id_tree::NodeId,
|
||||
}
|
||||
|
||||
pub enum Msg {
|
||||
AddOne,
|
||||
ToggleLight,
|
||||
ToggleCollapse,
|
||||
ExpandNode(id_tree::NodeId),
|
||||
}
|
||||
|
||||
impl Component for App {
|
||||
|
@ -27,19 +31,91 @@ impl Component for App {
|
|||
type Properties = ();
|
||||
|
||||
fn create(_: Self::Properties, link: ComponentLink<Self>) -> Self {
|
||||
let mut tree = id_tree::TreeBuilder::new().build();
|
||||
let root_id = tree
|
||||
.insert(
|
||||
id_tree::Node::new(NodeData {
|
||||
icon: None,
|
||||
label: "".into(),
|
||||
is_selected: false,
|
||||
is_expanded: false,
|
||||
has_caret: true,
|
||||
disabled: false,
|
||||
on_collapse: None,
|
||||
on_expand: None,
|
||||
data: 0,
|
||||
}),
|
||||
id_tree::InsertBehavior::AsRoot,
|
||||
)
|
||||
.unwrap();
|
||||
let dir1 = tree
|
||||
.insert(
|
||||
id_tree::Node::new(NodeData {
|
||||
icon: Some(IconName::FolderClose),
|
||||
label: "Directory 1".into(),
|
||||
is_selected: false,
|
||||
is_expanded: false,
|
||||
has_caret: true,
|
||||
disabled: false,
|
||||
on_collapse: Some(link.callback(|(node_id, _)| Msg::ExpandNode(node_id))),
|
||||
on_expand: Some(link.callback(|(node_id, _)| Msg::ExpandNode(node_id))),
|
||||
data: 1,
|
||||
}),
|
||||
id_tree::InsertBehavior::UnderNode(&root_id),
|
||||
)
|
||||
.unwrap();
|
||||
tree.insert(
|
||||
id_tree::Node::new(NodeData {
|
||||
icon: Some(IconName::Document),
|
||||
label: "File 1".into(),
|
||||
is_selected: false,
|
||||
is_expanded: false,
|
||||
has_caret: false,
|
||||
disabled: false,
|
||||
on_collapse: None,
|
||||
on_expand: None,
|
||||
data: 2,
|
||||
}),
|
||||
id_tree::InsertBehavior::UnderNode(&root_id),
|
||||
)
|
||||
.unwrap();
|
||||
tree.insert(
|
||||
id_tree::Node::new(NodeData {
|
||||
icon: None,
|
||||
label: "File 2".into(),
|
||||
is_selected: false,
|
||||
is_expanded: false,
|
||||
has_caret: false,
|
||||
disabled: false,
|
||||
on_collapse: None,
|
||||
on_expand: None,
|
||||
data: 3,
|
||||
}),
|
||||
id_tree::InsertBehavior::UnderNode(&dir1),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
App {
|
||||
link,
|
||||
counter: 0,
|
||||
dark_theme: true,
|
||||
collapsed: true,
|
||||
tree,
|
||||
node_dir1_id: dir1,
|
||||
}
|
||||
}
|
||||
|
||||
fn update(&mut self, msg: Self::Message) -> ShouldRender {
|
||||
match msg {
|
||||
Msg::AddOne => self.counter += 1,
|
||||
Msg::ToggleLight => self.dark_theme = !self.dark_theme,
|
||||
Msg::ToggleLight => self.dark_theme ^= true,
|
||||
Msg::ToggleCollapse => self.collapsed ^= true,
|
||||
Msg::ExpandNode(node_id) => {
|
||||
crate::log!("{:?}", node_id);
|
||||
crate::log!("{:?}", self.node_dir1_id);
|
||||
let node = self.tree.get_mut(&node_id).unwrap();
|
||||
node.data_mut().is_expanded ^= true;
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
@ -101,7 +177,7 @@ impl Component for App {
|
|||
</Collapse>
|
||||
</div>
|
||||
<div>
|
||||
<Tree />
|
||||
<Tree<i32> tree=self.tree.clone() />
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
|
23
src/icon.rs
23
src/icon.rs
|
@ -2,6 +2,12 @@ use yew::prelude::*;
|
|||
|
||||
include!(concat!(env!("OUT_DIR"), "/icon_svg_paths.rs"));
|
||||
|
||||
impl Default for IconName {
|
||||
fn default() -> Self {
|
||||
IconName::Blank
|
||||
}
|
||||
}
|
||||
|
||||
pub const SIZE_STANDARD: i32 = 16;
|
||||
pub const SIZE_LARGE: i32 = 20;
|
||||
|
||||
|
@ -21,7 +27,7 @@ pub struct Props {
|
|||
#[prop_or(16)]
|
||||
pub icon_size: i32,
|
||||
#[prop_or_default]
|
||||
pub onclick: Callback<MouseEvent>,
|
||||
pub onclick: Option<Callback<MouseEvent>>,
|
||||
}
|
||||
|
||||
impl Component for Icon {
|
||||
|
@ -59,23 +65,18 @@ impl Component for Icon {
|
|||
} else {
|
||||
SIZE_STANDARD
|
||||
};
|
||||
let icon_string = format!("{:?}", self.props.icon);
|
||||
|
||||
html! {
|
||||
<span class="bp3-icon" onclick={self.props.onclick.clone()}>
|
||||
<span class=class onclick?={self.props.onclick.clone()}>
|
||||
<svg
|
||||
fill={self.props.color.clone().unwrap_or_default()}
|
||||
data-icon={format!("{:?}", self.props.icon)}
|
||||
fill?={self.props.color.clone()}
|
||||
data-icon={icon_string.clone()}
|
||||
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()
|
||||
}
|
||||
}
|
||||
<desc>{self.props.title.clone().unwrap_or_else(|| icon_string)}</desc>
|
||||
{
|
||||
paths.iter()
|
||||
.map(|x| html! {
|
||||
|
|
104
src/tree.rs
104
src/tree.rs
|
@ -2,19 +2,33 @@ use crate::collapse::Collapse;
|
|||
use crate::icon::{Icon, IconName};
|
||||
use yew::prelude::*;
|
||||
|
||||
pub struct Tree {
|
||||
props: Props,
|
||||
pub struct Tree<T: Clone + PartialEq> {
|
||||
props: Props<T>,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Properties)]
|
||||
pub struct Props {
|
||||
pub struct Props<T: Clone + PartialEq> {
|
||||
#[prop_or_default]
|
||||
is_expanded: bool,
|
||||
pub is_expanded: bool,
|
||||
pub tree: id_tree::Tree<NodeData<T>>,
|
||||
}
|
||||
|
||||
impl Component for Tree {
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub struct NodeData<T: Clone + PartialEq> {
|
||||
pub disabled: bool,
|
||||
pub has_caret: bool,
|
||||
pub icon: Option<IconName>,
|
||||
pub is_expanded: bool,
|
||||
pub is_selected: bool,
|
||||
pub label: yew::virtual_dom::VNode,
|
||||
pub on_collapse: Option<Callback<(id_tree::NodeId, MouseEvent)>>,
|
||||
pub on_expand: Option<Callback<(id_tree::NodeId, MouseEvent)>>,
|
||||
pub data: T,
|
||||
}
|
||||
|
||||
impl<T: Clone + PartialEq + 'static> Component for Tree<T> {
|
||||
type Message = ();
|
||||
type Properties = Props;
|
||||
type Properties = Props<T>;
|
||||
|
||||
fn create(props: Self::Properties, _link: ComponentLink<Self>) -> Self {
|
||||
Tree { props }
|
||||
|
@ -34,12 +48,46 @@ impl Component for Tree {
|
|||
}
|
||||
|
||||
fn view(&self) -> Html {
|
||||
let mut nodes = Vec::new();
|
||||
|
||||
if let Some(root_id) = self.props.tree.root_node_id() {
|
||||
for node_id in self.props.tree.children_ids(root_id).unwrap() {
|
||||
let node = self.props.tree.get(node_id).unwrap();
|
||||
let data = node.data();
|
||||
let on_collapse = {
|
||||
let node_id = node_id.clone();
|
||||
data.on_collapse
|
||||
.clone()
|
||||
.map(move |x| x.reform(move |event| (node_id.clone(), event)))
|
||||
};
|
||||
let on_expand = {
|
||||
let node_id = node_id.clone();
|
||||
data.on_expand
|
||||
.clone()
|
||||
.map(move |x| x.reform(move |event| (node_id.clone(), event)))
|
||||
};
|
||||
|
||||
nodes.push(html! {
|
||||
<TreeNode
|
||||
disabled=data.disabled
|
||||
has_caret=data.has_caret
|
||||
icon=data.icon
|
||||
is_expanded=data.is_expanded
|
||||
is_selected=data.is_selected
|
||||
label=data.label.clone()
|
||||
on_collapse=on_collapse
|
||||
on_expand=on_expand
|
||||
>
|
||||
{"Example Stuff"}
|
||||
</TreeNode>
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
html! {
|
||||
<div class="bp3-tree">
|
||||
<ul class="bp3-tree-node-list">
|
||||
<TreeNode is_expanded=self.props.is_expanded has_caret=true>
|
||||
{"Example Stuff"}
|
||||
</TreeNode>
|
||||
{nodes}
|
||||
</ul>
|
||||
</div>
|
||||
}
|
||||
|
@ -53,10 +101,22 @@ pub struct TreeNode {
|
|||
#[derive(Clone, PartialEq, Properties)]
|
||||
pub struct TreeNodeProps {
|
||||
#[prop_or_default]
|
||||
pub is_expanded: bool,
|
||||
pub disabled: bool,
|
||||
#[prop_or_default]
|
||||
pub has_caret: bool,
|
||||
#[prop_or_default]
|
||||
pub icon: Option<IconName>,
|
||||
#[prop_or_default]
|
||||
pub is_expanded: bool,
|
||||
#[prop_or_default]
|
||||
pub is_selected: bool,
|
||||
#[prop_or_default]
|
||||
pub label: yew::virtual_dom::VNode,
|
||||
#[prop_or_default]
|
||||
pub on_collapse: Option<Callback<MouseEvent>>,
|
||||
#[prop_or_default]
|
||||
pub on_expand: Option<Callback<MouseEvent>>,
|
||||
#[prop_or_default]
|
||||
pub children: html::Children,
|
||||
}
|
||||
|
||||
|
@ -87,8 +147,27 @@ impl Component for TreeNode {
|
|||
<div class="bp3-tree-node-content">
|
||||
{
|
||||
if self.props.has_caret {
|
||||
let mut class = "bp3-tree-node-caret ".to_string();
|
||||
class.push_str(if self.props.is_expanded {
|
||||
"bp3-tree-node-caret-open"
|
||||
} else {
|
||||
"bp3-tree-node-caret-closed"
|
||||
});
|
||||
|
||||
html! {
|
||||
<Icon class="bp3-tree-node-caret" icon=IconName::ChevronRight />
|
||||
<Icon
|
||||
class=class
|
||||
icon=IconName::ChevronRight
|
||||
onclick={
|
||||
if self.props.disabled {
|
||||
None
|
||||
} else if self.props.is_expanded {
|
||||
self.props.on_collapse.clone()
|
||||
} else {
|
||||
self.props.on_expand.clone()
|
||||
}
|
||||
}
|
||||
/>
|
||||
}
|
||||
} else {
|
||||
html! {
|
||||
|
@ -96,7 +175,8 @@ impl Component for TreeNode {
|
|||
}
|
||||
}
|
||||
}
|
||||
{"content"}
|
||||
<Icon class="bp3-tree-node-icon" icon=self.props.icon.unwrap_or_default() />
|
||||
<span class="bp3-tree-node-label">{self.props.label.clone()}</span>
|
||||
</div>
|
||||
<Collapse is_open=self.props.is_expanded>
|
||||
{self.props.children.clone()}
|
||||
|
|
Loading…
Add table
Reference in a new issue