mirror of
https://github.com/yewprint/yewprint
synced 2024-11-22 03:23:03 +00:00
Real performance improvement
This commit is contained in:
parent
bf5f801fd1
commit
cf69038772
1 changed files with 109 additions and 64 deletions
173
src/tree.rs
173
src/tree.rs
|
@ -4,6 +4,7 @@ use crate::Intent;
|
|||
pub use id_tree::*;
|
||||
use std::cell::{Ref, RefCell, RefMut};
|
||||
use std::collections::hash_map::DefaultHasher;
|
||||
use std::collections::HashMap;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::rc::Rc;
|
||||
use yew::prelude::*;
|
||||
|
@ -42,6 +43,7 @@ impl<T> From<id_tree::Tree<NodeData<T>>> for TreeData<T> {
|
|||
|
||||
pub struct Tree<T: Clone> {
|
||||
props: Props<T>,
|
||||
previous_expanded_state: RefCell<HashMap<u64, bool>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Properties)]
|
||||
|
@ -50,11 +52,11 @@ pub struct Props<T: Clone> {
|
|||
pub is_expanded: bool,
|
||||
pub tree: TreeData<T>,
|
||||
#[prop_or_default]
|
||||
pub on_collapse: Option<Callback<(id_tree::NodeId, MouseEvent)>>,
|
||||
pub on_collapse: Option<Callback<(NodeId, MouseEvent)>>,
|
||||
#[prop_or_default]
|
||||
pub on_expand: Option<Callback<(id_tree::NodeId, MouseEvent)>>,
|
||||
pub on_expand: Option<Callback<(NodeId, MouseEvent)>>,
|
||||
#[prop_or_default]
|
||||
pub onclick: Option<Callback<(id_tree::NodeId, MouseEvent)>>,
|
||||
pub onclick: Option<Callback<(NodeId, MouseEvent)>>,
|
||||
}
|
||||
|
||||
pub struct NodeData<T> {
|
||||
|
@ -109,7 +111,10 @@ impl<T: Clone + PartialEq + 'static> Component for Tree<T> {
|
|||
type Properties = Props<T>;
|
||||
|
||||
fn create(props: Self::Properties, _link: ComponentLink<Self>) -> Self {
|
||||
Tree { props }
|
||||
Tree {
|
||||
props,
|
||||
previous_expanded_state: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn update(&mut self, _msg: Self::Message) -> ShouldRender {
|
||||
|
@ -136,7 +141,9 @@ impl<T: Clone + PartialEq + 'static> Component for Tree<T> {
|
|||
|
||||
html! {
|
||||
<div class="bp3-tree">
|
||||
{nodes}
|
||||
<ul class="bp3-tree-node-list">
|
||||
{nodes}
|
||||
</ul>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
@ -145,42 +152,28 @@ impl<T: Clone + PartialEq + 'static> Component for Tree<T> {
|
|||
impl<T: Clone> Tree<T> {
|
||||
fn render_children(&self, node_id: &NodeId, depth: u32) -> yew::virtual_dom::VNode {
|
||||
let tree = self.props.tree.borrow();
|
||||
let node = tree.get(node_id).unwrap();
|
||||
let children = node.children();
|
||||
|
||||
let nodes = tree
|
||||
.children_ids(node_id)
|
||||
.unwrap()
|
||||
let nodes = children
|
||||
.iter()
|
||||
.map(|node_id| {
|
||||
let key = {
|
||||
let mut hasher = DefaultHasher::new();
|
||||
node_id.hash(&mut hasher);
|
||||
hasher.finish()
|
||||
};
|
||||
let node = tree.get(node_id).unwrap();
|
||||
let data = node.data();
|
||||
let on_collapse = {
|
||||
let node_id = node_id.clone();
|
||||
self.props.on_collapse.clone().map(move |x| {
|
||||
x.reform(move |event: MouseEvent| {
|
||||
event.stop_propagation();
|
||||
(node_id.clone(), event)
|
||||
})
|
||||
})
|
||||
let previous_is_expanded = self
|
||||
.previous_expanded_state
|
||||
.borrow_mut()
|
||||
.insert(key, data.is_expanded);
|
||||
let inner_nodes = if !data.is_expanded && !previous_is_expanded.unwrap_or(true) {
|
||||
Default::default()
|
||||
} else {
|
||||
self.render_children(node_id, depth + 1)
|
||||
};
|
||||
let on_expand = {
|
||||
let node_id = node_id.clone();
|
||||
self.props.on_expand.clone().map(move |x| {
|
||||
x.reform(move |event: MouseEvent| {
|
||||
event.stop_propagation();
|
||||
(node_id.clone(), event)
|
||||
})
|
||||
})
|
||||
};
|
||||
let onclick = {
|
||||
let node_id = node_id.clone();
|
||||
self.props
|
||||
.onclick
|
||||
.clone()
|
||||
.map(move |x| x.reform(move |event| (node_id.clone(), event)))
|
||||
};
|
||||
let inner_nodes = self.render_children(node_id, depth + 1);
|
||||
let mut hasher = DefaultHasher::new();
|
||||
node_id.hash(&mut hasher);
|
||||
let key = hasher.finish();
|
||||
|
||||
html! {
|
||||
<TreeNode
|
||||
|
@ -193,10 +186,11 @@ impl<T: Clone> Tree<T> {
|
|||
is_selected=data.is_selected
|
||||
label=data.label.clone()
|
||||
secondary_label=data.secondary_label.clone()
|
||||
on_collapse=on_collapse
|
||||
on_expand=on_expand
|
||||
onclick=onclick
|
||||
on_collapse=self.props.on_collapse.clone()
|
||||
on_expand=self.props.on_expand.clone()
|
||||
onclick=self.props.onclick.clone()
|
||||
depth=depth
|
||||
node_id=node_id.clone()
|
||||
key=key
|
||||
>
|
||||
{inner_nodes}
|
||||
|
@ -205,20 +199,19 @@ impl<T: Clone> Tree<T> {
|
|||
})
|
||||
.collect::<Html>();
|
||||
|
||||
html! {
|
||||
<ul class="bp3-tree-node-list">
|
||||
{nodes}
|
||||
</ul>
|
||||
}
|
||||
return nodes;
|
||||
}
|
||||
}
|
||||
|
||||
struct TreeNode {
|
||||
props: TreeNodeProps,
|
||||
handler_caret_click: Callback<MouseEvent>,
|
||||
handler_click: Callback<MouseEvent>,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Properties)]
|
||||
#[derive(Clone, Properties)]
|
||||
struct TreeNodeProps {
|
||||
node_id: NodeId,
|
||||
disabled: bool,
|
||||
has_caret: bool,
|
||||
icon: Option<IconName>,
|
||||
|
@ -228,27 +221,85 @@ struct TreeNodeProps {
|
|||
is_selected: bool,
|
||||
label: yew::virtual_dom::VNode,
|
||||
secondary_label: Option<yew::virtual_dom::VNode>,
|
||||
on_collapse: Option<Callback<MouseEvent>>,
|
||||
on_expand: Option<Callback<MouseEvent>>,
|
||||
onclick: Option<Callback<MouseEvent>>,
|
||||
on_collapse: Option<Callback<(id_tree::NodeId, MouseEvent)>>,
|
||||
on_expand: Option<Callback<(id_tree::NodeId, MouseEvent)>>,
|
||||
onclick: Option<Callback<(id_tree::NodeId, MouseEvent)>>,
|
||||
children: html::Children,
|
||||
depth: u32,
|
||||
}
|
||||
|
||||
impl PartialEq for TreeNodeProps {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.node_id == other.node_id
|
||||
&& self.disabled == other.disabled
|
||||
&& self.has_caret == other.has_caret
|
||||
&& self.icon == other.icon
|
||||
&& self.icon_color == other.icon_color
|
||||
&& self.icon_intent == other.icon_intent
|
||||
&& self.is_expanded == other.is_expanded
|
||||
&& self.is_selected == other.is_selected
|
||||
&& self.label == other.label
|
||||
&& self.secondary_label == other.secondary_label
|
||||
&& self.on_collapse == other.on_collapse
|
||||
&& self.on_expand == other.on_expand
|
||||
&& self.onclick == other.onclick
|
||||
&& self.depth == other.depth
|
||||
&& (!other.is_expanded || self.children == other.children)
|
||||
}
|
||||
}
|
||||
|
||||
enum TreeNodeMessage {
|
||||
CaretClick(MouseEvent),
|
||||
Click(MouseEvent),
|
||||
}
|
||||
|
||||
impl Component for TreeNode {
|
||||
type Message = ();
|
||||
type Message = TreeNodeMessage;
|
||||
type Properties = TreeNodeProps;
|
||||
|
||||
fn create(props: Self::Properties, _link: ComponentLink<Self>) -> Self {
|
||||
TreeNode { props }
|
||||
fn create(props: Self::Properties, link: ComponentLink<Self>) -> Self {
|
||||
TreeNode {
|
||||
handler_caret_click: link.callback(TreeNodeMessage::CaretClick),
|
||||
handler_click: link.callback(TreeNodeMessage::Click),
|
||||
props,
|
||||
}
|
||||
}
|
||||
|
||||
fn update(&mut self, _msg: Self::Message) -> ShouldRender {
|
||||
true
|
||||
fn update(&mut self, msg: Self::Message) -> ShouldRender {
|
||||
if self.props.disabled {
|
||||
return false;
|
||||
}
|
||||
|
||||
match msg {
|
||||
TreeNodeMessage::CaretClick(event) => {
|
||||
if self.props.is_expanded {
|
||||
if let Some(on_collapse) = self.props.on_collapse.as_ref() {
|
||||
event.stop_propagation();
|
||||
on_collapse.emit((self.props.node_id.clone(), event));
|
||||
}
|
||||
} else if let Some(on_expand) = self.props.on_expand.as_ref() {
|
||||
event.stop_propagation();
|
||||
on_expand.emit((self.props.node_id.clone(), event));
|
||||
}
|
||||
}
|
||||
TreeNodeMessage::Click(event) => {
|
||||
if let Some(onclick) = self.props.onclick.as_ref() {
|
||||
onclick.emit((self.props.node_id.clone(), event));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
fn change(&mut self, props: Self::Properties) -> ShouldRender {
|
||||
if self.props != props {
|
||||
// crate::log!(
|
||||
// "rerender {:?} {} {:?}",
|
||||
// self.props.node_id,
|
||||
// self.props.children == props.children,
|
||||
// self.props.icon,
|
||||
// );
|
||||
self.props = props;
|
||||
true
|
||||
} else {
|
||||
|
@ -266,7 +317,7 @@ impl Component for TreeNode {
|
|||
|
||||
html! {
|
||||
<li class=container_class>
|
||||
<div class=content_class onclick?={self.props.onclick.clone()}>
|
||||
<div class=content_class onclick=self.handler_click.clone()>
|
||||
{
|
||||
if self.props.has_caret {
|
||||
let mut class = Classes::from("bp3-tree-node-caret");
|
||||
|
@ -280,15 +331,7 @@ impl Component for TreeNode {
|
|||
<Icon
|
||||
class=class.to_string()
|
||||
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()
|
||||
}
|
||||
}
|
||||
onclick=self.handler_caret_click.clone()
|
||||
/>
|
||||
}
|
||||
} else {
|
||||
|
@ -313,7 +356,9 @@ impl Component for TreeNode {
|
|||
}
|
||||
</div>
|
||||
<Collapse is_open=self.props.is_expanded>
|
||||
{self.props.children.clone()}
|
||||
<ul class="bp3-tree-node-list">
|
||||
{self.props.children.clone()}
|
||||
</ul>
|
||||
</Collapse>
|
||||
</li>
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue