Skip to content

Commit 704ceeb

Browse files
committed
chore(侧边栏): 调整侧边栏配置
1 parent 3e7c339 commit 704ceeb

File tree

9 files changed

+300
-224
lines changed

9 files changed

+300
-224
lines changed

src/components/sidebar.rs

+154-83
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use std::collections::HashMap;
12
use yew::prelude::*;
23

34
#[derive(Clone, PartialEq, Properties)]
@@ -7,55 +8,133 @@ pub struct FileNode {
78
pub children: Option<Vec<FileNode>>,
89
}
910

11+
fn load_data() -> Vec<FileNode> {
12+
let file_tree = vec![FileNode {
13+
name: "Yew 篇".to_string(),
14+
is_folder: true,
15+
children: Some(vec![
16+
FileNode {
17+
name: "环境配置".to_string(),
18+
is_folder: false,
19+
children: None,
20+
},
21+
FileNode {
22+
name: "第一个静态页面".to_string(),
23+
is_folder: false,
24+
children: None,
25+
},
26+
FileNode {
27+
name: "HTML".to_string(),
28+
is_folder: true,
29+
children: Some(vec![
30+
FileNode {
31+
name: "html! 宏".to_string(),
32+
is_folder: false,
33+
children: None,
34+
},
35+
FileNode {
36+
name: "事件".to_string(),
37+
is_folder: true,
38+
children: Some(vec![
39+
FileNode {
40+
name: "事件监听器".to_string(),
41+
is_folder: false,
42+
children: None,
43+
},
44+
FileNode {
45+
name: "事件冒泡".to_string(),
46+
is_folder: false,
47+
children: None,
48+
},
49+
]),
50+
},
51+
]),
52+
},
53+
]),
54+
}];
55+
file_tree
56+
}
57+
58+
#[function_component(SearchIcon)]
59+
fn search_icon() -> Html {
60+
html! {
61+
<svg
62+
xmlns="http://www.w3.org/2000/svg"
63+
class="search-icon"
64+
fill="none"
65+
viewBox="0 0 24 24"
66+
stroke="currentColor"
67+
>
68+
<path
69+
stroke-linecap="round"
70+
stroke-linejoin="round"
71+
stroke-width="2"
72+
d="M15.75 15.75L19.5 19.5M10.5 18.75a8.25 8.25 0 1 1 0-16.5 8.25 8.25 0 0 1 0 16.5z"
73+
/>
74+
</svg>
75+
}
76+
}
77+
78+
79+
#[function_component(SearchInput)]
80+
fn search_input() -> Html {
81+
html! {
82+
<input
83+
type="text"
84+
class="search-input"
85+
placeholder="查找"
86+
/>
87+
}
88+
}
89+
90+
91+
#[derive(Properties, PartialEq)]
92+
pub struct FolderIconProps {
93+
pub used: bool,
94+
}
95+
// 定义 FolderIcon 组件
96+
#[function_component(FolderIcon)]
97+
pub fn folder_icon(props: &FolderIconProps) -> Html {
98+
// 根据 `used` 状态生成类名
99+
let class_name = if props.used {
100+
"folder-icon open"
101+
} else {
102+
"folder-icon"
103+
};
104+
105+
html! {
106+
<svg class={class_name} xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
107+
{
108+
if props.used {
109+
html! {
110+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 9l6 6 6-6" /> // 向下箭头
111+
}
112+
} else {
113+
html! {
114+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 6l6 6-6 6" /> // 向右箭头
115+
}
116+
}
117+
}
118+
</svg>
119+
}
120+
}
121+
122+
123+
#[function_component(FileIcon)]
124+
pub fn file_icon() -> Html {
125+
html! {
126+
<svg class="file-icon" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
127+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4" />
128+
</svg>
129+
}
130+
}
131+
10132
#[function_component(Sidebar)]
11133
pub fn sidebar() -> Html {
12-
let file_tree = vec![
13-
FileNode {
14-
name: "Yew 篇".to_string(),
15-
is_folder: true,
16-
children: Some(vec![
17-
FileNode {
18-
name: "环境配置".to_string(),
19-
is_folder: false,
20-
children: None,
21-
},
22-
FileNode {
23-
name: "第一个静态页面".to_string(),
24-
is_folder: false,
25-
children: None,
26-
},
27-
FileNode {
28-
name: "HTML".to_string(),
29-
is_folder: true,
30-
children: Some(vec![
31-
FileNode {
32-
name: "html! 宏".to_string(),
33-
is_folder: false,
34-
children: None,
35-
},
36-
FileNode {
37-
name: "事件".to_string(),
38-
is_folder: true,
39-
children: Some(vec![
40-
FileNode {
41-
name: "事件监听器".to_string(),
42-
is_folder: false,
43-
children: None,
44-
},
45-
FileNode {
46-
name: "事件冒泡".to_string(),
47-
is_folder: false,
48-
children: None,
49-
},
50-
]),
51-
},
52-
]),
53-
},
54-
]),
55-
},
56-
];
57-
58-
let is_expanded = use_state(|| true);
134+
let file_tree = load_data();
135+
136+
// 初始化文件夹的展开状态
137+
let expanded_state = use_state(|| HashMap::<String, bool>::new());
59138

60139
html! {
61140
<div class="sidebar">
@@ -65,68 +144,60 @@ pub fn sidebar() -> Html {
65144
</div>
66145
<div class="sidebar-search">
67146
<div class="search-container">
68-
<input
69-
type="text"
70-
class="search-input"
71-
placeholder="查找"
72-
/>
73-
<svg
74-
xmlns="http://www.w3.org/2000/svg"
75-
class="search-icon"
76-
fill="none"
77-
viewBox="0 0 24 24"
78-
stroke="currentColor"
79-
>
80-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15.75 15.75L19.5 19.5M10.5 18.75a8.25 8.25 0 1 1 0-16.5 8.25 8.25 0 0 1 0 16.5z" />
81-
</svg>
147+
<SearchInput />
148+
<SearchIcon />
82149
</div>
83150
</div>
84151
<div class="sidebar-content">
85-
{file_tree.iter().map(|node| render_file_node(node, is_expanded.clone())).collect::<Html>()}
152+
{file_tree.iter().map(|node| render_file_node(node, expanded_state.clone())).collect::<Html>()}
86153
</div>
87154
</div>
88155
}
89156
}
90157

91-
fn render_file_node(file_node: &FileNode, is_expanded: UseStateHandle<bool>) -> Html {
158+
fn render_file_node(file_node: &FileNode, expanded_state: UseStateHandle<HashMap<String, bool>>) -> Html {
159+
let file_name = file_node.name.clone();
160+
161+
// 获取当前文件夹的展开状态
162+
let is_expanded = expanded_state
163+
.get(&file_name)
164+
.cloned()
165+
.unwrap_or(false);
166+
167+
// 点击事件:更新当前文件夹的展开状态
92168
let toggle = {
93-
let is_expanded = is_expanded.clone();
94-
Callback::from(move |_| is_expanded.set(!*is_expanded))
169+
let expanded_state = expanded_state.clone();
170+
let file_name = file_name.clone();
171+
Callback::from(move |_| {
172+
let mut state = (*expanded_state).clone();
173+
let is_expanded = state.entry(file_name.clone()).or_insert(false);
174+
*is_expanded = !*is_expanded;
175+
expanded_state.set(state);
176+
})
95177
};
96178

97179
html! {
98180
<div class="file-node">
99181
<div class="file-node-header" onclick={toggle}>
100182
{
101183
if file_node.is_folder {
102-
if *is_expanded {
103-
html! {
104-
<svg class="folder-icon open" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
105-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 9l6 6 6-6" />
106-
</svg>
107-
}
184+
if is_expanded {
185+
html! {<FolderIcon used={true} />}
108186
} else {
109-
html! {
110-
<svg class="folder-icon" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
111-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 6l6 6-6 6" />
112-
</svg>
113-
}
187+
188+
html! {<FolderIcon used={false} />}
114189
}
115190
} else {
116-
html! {
117-
<svg class="file-icon" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
118-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4" />
119-
</svg>
120-
}
191+
html! {<FileIcon />}
121192
}
122193
}
123194
<span class="file-node-name">{ &file_node.name }</span>
124195
</div>
125196
{
126-
if *is_expanded && file_node.is_folder {
197+
if is_expanded && file_node.is_folder {
127198
html! {
128199
<div class="file-node-children">
129-
{file_node.children.as_ref().unwrap_or(&vec![]).iter().map(|child| render_file_node(child, is_expanded.clone())).collect::<Html>()}
200+
{file_node.children.as_ref().unwrap_or(&vec![]).iter().map(|child| render_file_node(child, expanded_state.clone())).collect::<Html>()}
130201
</div>
131202
}
132203
} else {

src/main.rs

-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
mod markdown;
22
mod utils;
33
mod components;
4-
mod model;
54

65
use crate::components::content::MainContent;
76
use crate::components::navigation::Navigation;

src/model/file_node.rs

-15
This file was deleted.

src/model/file_tree.rs

-55
This file was deleted.

src/model/mod.rs

-2
This file was deleted.

0 commit comments

Comments
 (0)