xfy 65b15509a7 chore(wasm): 清理 wasm32 target 下残留的 clippy lint
这些 lint 在 server build(默认 --all-features)下被 cfg 剥离故未暴露,
仅 wasm32 web-only target 触发。逐项清理:

- footer.rs: 两处 redundant closure(web_sys::window 直接传函数引用、
  sync_visible 直接传闭包)+ let _ = unit 绑定(scroll_to_*)。
- hooks/event_listener.rs: 复杂类型 Rc<RefCell<Option<(Closure,T)>>>
  抽 type ListenerState<T> 别名。
- system.rs: 两处单模式 match { Ok=>.., _=>{} } 改 if let。
- write.rs: split(|c| matches!(c,...)) 改 split([char; N]) 数组模式。
- codemirror_bridge.rs: let _ = instance.destroy() 删 unit 绑定,
  与 tiptap_bridge.rs 同处写法对齐。

wasm32 与 server 两 target clippy -D warnings 均通过。
2026-07-02 17:15:27 +08:00

112 lines
4.1 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//! 页脚组件
//!
//! 提供站点版权信息,并在用户向下滚动超过一屏后显示"回到顶部"悬浮按钮。
//! 回到顶部的滚动监听与平滑滚动逻辑仅在 WASM 前端生效。
#[cfg(target_arch = "wasm32")]
use crate::hooks::event_listener::use_event_listener;
use dioxus::prelude::*;
/// 页脚与回到顶部按钮组件。
///
/// Props无。
/// 关键行为:
/// - 监听窗口滚动,超过一屏时显示回到顶部按钮
/// - 点击按钮平滑滚动到顶部,并清理 URL 中的 `#`
/// - 滚动监听与平滑滚动仅在 `target_arch = "wasm32"` 下执行
#[component]
#[allow(unused_mut)]
pub fn Footer() -> Element {
let mut visible = use_signal(|| false);
// 根据 window 当前滚动位置同步 visible注册监听后立即调用一次避免首屏漏判
// 滚动事件回调里也复用同一份判断逻辑。
let mut sync_visible = move || {
#[cfg(target_arch = "wasm32")]
{
if let Some(w) = web_sys::window() {
let threshold = w
.inner_height()
.ok()
.and_then(|h| h.as_f64())
.unwrap_or(0.0);
let scroll_y = w.scroll_y().unwrap_or(0.0);
visible.set(scroll_y > threshold);
}
}
};
// 注册 scroll 监听:注册 / 卸载清理由 use_event_listener 负责。
// 仅 WASM 端调用server 端 use_event_listener 是 noop但 acquire 闭包内的
// web_sys 在非 wasm 下不可解析,故整块 cfghook 数量在 server build 中不影响,
// 因为 server 端该组件只跑一次 SSR
#[cfg(target_arch = "wasm32")]
use_event_listener(
web_sys::window,
"scroll",
// 滚动事件触发时复用同样的阈值判断。
sync_visible,
);
// 挂载时根据当前滚动位置初始化一次按钮可见性。
use_effect(move || {
sync_visible();
});
// 根据 visible 动态切换按钮显示/隐藏样式
let btn_class = use_memo(move || {
let base = "fixed bottom-16 right-8 z-50 w-10 h-10 rounded-full bg-paper-entry border border-paper-border shadow-sm flex items-center justify-center cursor-pointer transition-all duration-300 text-paper-secondary hover:text-paper-accent";
if visible() {
format!("{} opacity-100 translate-y-0", base)
} else {
format!("{} opacity-0 translate-y-2 pointer-events-none", base)
}
});
rsx! {
footer { class: "w-full border-t border-paper-border mt-auto",
div { class: "max-w-3xl mx-auto px-6 py-5 flex items-center justify-between text-sm text-paper-secondary",
span { "© 2026 Yggdrasil" }
}
}
a {
class: "{btn_class}",
href: "#top",
aria_label: "go to top",
title: "Go to Top (Alt + G)",
accesskey: "g",
onclick: move |evt| {
evt.prevent_default();
scroll_to_top();
},
svg {
xmlns: "http://www.w3.org/2000/svg",
height: "24px",
view_box: "0 -960 960 960",
width: "24px",
fill: "currentColor",
path { d: "m296-224-56-56 240-240 240 240-56 56-184-183-184 183Zm0-240-56-56 240-240 240 240-56 56-184-183-184 183Z" }
}
}
}
}
/// 平滑滚动到页面顶部,并清理 history 中的 `#` 哈希。
///
/// 仅在 `target_arch = "wasm32"` 下执行实际滚动SSR 环境中为空操作。
fn scroll_to_top() {
#[cfg(target_arch = "wasm32")]
{
if let Some(window) = web_sys::window() {
let options = web_sys::ScrollToOptions::new();
options.set_top(0.0);
options.set_behavior(web_sys::ScrollBehavior::Smooth);
window.scroll_to_with_scroll_to_options(&options);
if let Ok(history) = window.history() {
let _ = history.replace_state_with_url(&wasm_bindgen::JsValue::NULL, "", Some(" "));
}
}
}
}