Implement Rust web sidebar title and tree interactions
This commit is contained in:
@@ -102,13 +102,13 @@ pub fn build_workspace_shell_projection(
|
||||
id: "trash".into(),
|
||||
label: "垃圾箱".into(),
|
||||
href: "/trash".into(),
|
||||
icon: "⌫".into(),
|
||||
icon: "delete".into(),
|
||||
},
|
||||
WorkspaceShellEntry {
|
||||
id: "templates".into(),
|
||||
label: "模板中心".into(),
|
||||
href: "/templates".into(),
|
||||
icon: "◇".into(),
|
||||
icon: "inventory_2".into(),
|
||||
},
|
||||
],
|
||||
}
|
||||
@@ -321,6 +321,10 @@ mod tests {
|
||||
assert!(html.contains("/documents/doc_root?workspaceId=ws_demo"));
|
||||
assert!(html.contains("data-testid=\"wolai-sidebar-create-page\""));
|
||||
assert!(html.contains("data-mnote-action=\"create-page\""));
|
||||
assert!(html.contains("class=\"mnote-symbol"));
|
||||
assert!(html.contains("data-icon=\"home\""));
|
||||
assert!(html.contains("data-icon=\"star\""));
|
||||
assert!(html.contains("data-icon=\"delete\""));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -422,9 +426,9 @@ pub fn render_workspace_shell_sidebar_html(
|
||||
.iter()
|
||||
.map(|entry| {
|
||||
format!(
|
||||
r#"<a href="{}" class="wolai-footer-entry"><span>{}</span>{}</a>"#,
|
||||
r#"<a href="{}" class="wolai-footer-entry">{}{}</a>"#,
|
||||
escape_html(&entry.href),
|
||||
escape_html(&entry.icon),
|
||||
render_symbol(&entry.icon, "wolai-footer-icon"),
|
||||
escape_html(&entry.label),
|
||||
)
|
||||
})
|
||||
@@ -432,7 +436,9 @@ pub fn render_workspace_shell_sidebar_html(
|
||||
.join("");
|
||||
|
||||
format!(
|
||||
r#"<section class="wolai-sidebar-section wolai-starred-section" aria-label="星标置顶"><div class="wolai-section-title"><span class="wolai-section-icon">★</span>星标置顶<span class="wolai-section-caret">⌄</span></div>{starred_rows}</section><section class="wolai-sidebar-section wolai-my-pages-section" aria-label="页面树" data-testid="wolai-sidebar-page-tree-shell"><div class="wolai-sidebar-tabs" data-testid="wolai-sidebar-tree-tabs" role="tablist" aria-label="页面树和文件树"><button type="button" class="wolai-sidebar-tab wolai-sidebar-tab-active" data-mnote-sidebar-tree-tab="page" aria-selected="true" aria-controls="wolai-sidebar-page-tree-panel">我的页面</button><button type="button" class="wolai-sidebar-tab wolai-sidebar-tab-muted" data-mnote-sidebar-tree-tab="filetree" aria-selected="false" aria-controls="wolai-sidebar-file-tree-panel"><span class="wolai-folder-icon">▱</span>Explorer</button><span class="wolai-section-caret">⌄</span><button type="button" class="wolai-section-add" data-testid="wolai-sidebar-create-page" data-mnote-action="create-page" data-workspace-id="{}" title="新建页面" aria-label="新建页面">+</button></div><div class="wolai-sidebar-tab-panels"><div id="wolai-sidebar-page-tree-panel" class="wolai-sidebar-tab-panel" data-mnote-sidebar-tree-panel="page">{my_pages}</div><div id="wolai-sidebar-file-tree-panel" class="wolai-sidebar-tab-panel" data-mnote-sidebar-tree-panel="filetree" hidden>{file_tree_content}</div></div></section><div class="wolai-sidebar-footer">{bottom_entries}</div>"#,
|
||||
r#"<section class="wolai-sidebar-section wolai-starred-section" aria-label="星标置顶"><div class="wolai-section-title">{}星标置顶<span class="wolai-section-caret">⌄</span></div>{starred_rows}</section><section class="wolai-sidebar-section wolai-my-pages-section" aria-label="页面树" data-testid="wolai-sidebar-page-tree-shell"><div class="wolai-sidebar-tabs" data-testid="wolai-sidebar-tree-tabs" role="tablist" aria-label="页面树和文件树"><button type="button" class="wolai-sidebar-tab wolai-sidebar-tab-active" data-mnote-sidebar-tree-tab="page" aria-selected="true" aria-controls="wolai-sidebar-page-tree-panel">我的页面</button><button type="button" class="wolai-sidebar-tab wolai-sidebar-tab-muted" data-mnote-sidebar-tree-tab="filetree" aria-selected="false" aria-controls="wolai-sidebar-file-tree-panel">{}Explorer</button><span class="wolai-section-caret">⌄</span><button type="button" class="wolai-section-add" data-testid="wolai-sidebar-create-page" data-mnote-action="create-page" data-workspace-id="{}" title="新建页面" aria-label="新建页面">+</button></div><div class="wolai-sidebar-tab-panels"><div id="wolai-sidebar-page-tree-panel" class="wolai-sidebar-tab-panel" data-mnote-sidebar-tree-panel="page">{my_pages}</div><div id="wolai-sidebar-file-tree-panel" class="wolai-sidebar-tab-panel" data-mnote-sidebar-tree-panel="filetree" hidden>{file_tree_content}</div></div></section><div class="wolai-sidebar-footer">{bottom_entries}</div>"#,
|
||||
render_symbol("star", "wolai-section-icon"),
|
||||
render_symbol("folder_open", "wolai-folder-icon"),
|
||||
escape_html(&projection.workspace_id),
|
||||
)
|
||||
}
|
||||
@@ -455,16 +461,55 @@ fn render_item_row(item: &WorkspaceShellItem) -> String {
|
||||
""
|
||||
};
|
||||
format!(
|
||||
r#"<a class="wolai-page-row{active_class}" href="{}" data-testid="wolai-sidebar-row" data-node-id="{}"{parent_attr} data-depth="{}" data-active="{}"{aria_current}{depth_style}><span class="wolai-row-caret" aria-hidden="true">›</span><span class="wolai-row-icon">{}</span><span class="wolai-row-title">{}</span></a>"#,
|
||||
r#"<a class="wolai-page-row{active_class}" href="{}" data-testid="wolai-sidebar-row" data-node-id="{}" data-document-id="{}"{parent_attr} data-depth="{}" data-active="{}"{aria_current}{depth_style}><span class="wolai-row-caret" aria-hidden="true">›</span><span class="wolai-row-icon">{}</span><span class="wolai-row-title">{}</span></a>"#,
|
||||
escape_html(&item.href),
|
||||
escape_html(&item.id),
|
||||
escape_html(&item.id),
|
||||
item.depth,
|
||||
item.active,
|
||||
escape_html(item.icon.as_deref().unwrap_or("▣")),
|
||||
render_symbol(item_icon_name(item.icon.as_deref()), "wolai-row-symbol"),
|
||||
escape_html(&item.title),
|
||||
)
|
||||
}
|
||||
|
||||
fn item_icon_name(icon: Option<&str>) -> &'static str {
|
||||
match icon.unwrap_or_default().trim() {
|
||||
"star" | "★" => "star",
|
||||
"folder" | "folder_open" | "▱" => "folder_open",
|
||||
"delete" | "trash" | "⌫" => "delete",
|
||||
"inventory_2" | "template" | "◇" => "inventory_2",
|
||||
"home" | "page" | "▣" | "⌂" | "" => "home",
|
||||
_ => "home",
|
||||
}
|
||||
}
|
||||
|
||||
fn render_symbol(icon: &str, class_name: &str) -> String {
|
||||
let icon = item_icon_name(Some(icon));
|
||||
let body = match icon {
|
||||
"delete" => {
|
||||
r#"<path d="M5 6h14"></path><path d="M9 6V4h6v2"></path><path d="M8 6l1 14h6l1-14"></path><path d="M10.5 10v6"></path><path d="M13.5 10v6"></path>"#
|
||||
}
|
||||
"folder_open" => {
|
||||
r#"<path d="M3.5 6.5h6l2 2h9"></path><path d="M4 8.5v9.5h14.5l2-7.5H7l-3 7.5"></path>"#
|
||||
}
|
||||
"inventory_2" => {
|
||||
r#"<path d="M4 6h16v4H4z"></path><path d="M6 10v9h12v-9"></path><path d="M9 14h6"></path>"#
|
||||
}
|
||||
"star" => {
|
||||
r#"<path d="m12 3 2.5 5.2 5.7.8-4.1 4 1 5.7-5.1-2.7-5.1 2.7 1-5.7-4.1-4 5.7-.8z"></path>"#
|
||||
}
|
||||
_ => {
|
||||
r#"<path d="M4 10.5 12 3l8 7.5"></path><path d="M6.5 9.5V21h11V9.5"></path><path d="M9.5 21v-6h5v6"></path>"#
|
||||
}
|
||||
};
|
||||
format!(
|
||||
r#"<svg class="mnote-symbol {}" data-icon="{}" viewBox="0 0 24 24" aria-hidden="true" focusable="false">{}</svg>"#,
|
||||
escape_html(class_name),
|
||||
escape_html(icon),
|
||||
body
|
||||
)
|
||||
}
|
||||
|
||||
fn escape_html(value: &str) -> String {
|
||||
value
|
||||
.replace('&', "&")
|
||||
|
||||
Reference in New Issue
Block a user