Compare commits

...

2 Commits

Author SHA1 Message Date
spinline
a08fd9698f refactor: açılır menüler leptos-use::on_click_outside ile modernize edildi, şeffaf backdrop katmanları kaldırıldı
All checks were successful
Build MIPS Binary / build (push) Successful in 5m52s
2026-02-08 20:16:05 +03:00
spinline
7d46dbd437 refactor: tema yönetimi leptos-use::use_local_storage ile reaktif hale getirildi
All checks were successful
Build MIPS Binary / build (push) Successful in 4m28s
2026-02-08 20:02:01 +03:00
3 changed files with 32 additions and 53 deletions

View File

@@ -55,3 +55,4 @@ js-sys = "0.3.85"
base64 = "0.22.1" base64 = "0.22.1"
serde-wasm-bindgen = "0.6.5" serde-wasm-bindgen = "0.6.5"
leptos-use = "0.13" leptos-use = "0.13"
codee = "0.2"

View File

@@ -1,4 +1,5 @@
use leptos::*; use leptos::*;
use leptos_use::on_click_outside;
#[component] #[component]
pub fn ContextMenu( pub fn ContextMenu(
@@ -8,6 +9,10 @@ pub fn ContextMenu(
on_close: Callback<()>, on_close: Callback<()>,
on_action: Callback<(String, String)>, // (Action, Hash) on_action: Callback<(String, String)>, // (Action, Hash)
) -> impl IntoView { ) -> impl IntoView {
let container_ref = create_node_ref::<html::Div>();
let _ = on_click_outside(container_ref, move |_| on_close.call(()));
let handle_action = move |action: &str| { let handle_action = move |action: &str| {
let hash = torrent_hash.clone(); let hash = torrent_hash.clone();
let action_str = action.to_string(); let action_str = action.to_string();
@@ -22,16 +27,8 @@ pub fn ContextMenu(
} }
view! { view! {
// Backdrop to catch clicks outside
<div
class="fixed inset-0 z-[99] cursor-default"
role="button"
tabindex="-1"
on:click=move |_| on_close.call(())
on:contextmenu=move |e| e.prevent_default()
></div>
<div <div
node_ref=container_ref
class="fixed z-[100] min-w-[200px] animate-in fade-in zoom-in-95 duration-100" class="fixed z-[100] min-w-[200px] animate-in fade-in zoom-in-95 duration-100"
style=format!("left: {}px; top: {}px", position.0, position.1) style=format!("left: {}px; top: {}px", position.0, position.1)
on:contextmenu=move |e| e.prevent_default() on:contextmenu=move |e| e.prevent_default()

View File

@@ -1,4 +1,7 @@
use leptos::*; use leptos::*;
use leptos_use::on_click_outside;
use leptos_use::storage::use_local_storage;
use codee::string::FromToStringCodec;
use shared::GlobalLimitRequest; use shared::GlobalLimitRequest;
fn format_bytes(bytes: i64) -> String { fn format_bytes(bytes: i64) -> String {
@@ -26,34 +29,19 @@ pub fn StatusBar() -> impl IntoView {
let store = use_context::<crate::store::TorrentStore>().expect("store not provided"); let store = use_context::<crate::store::TorrentStore>().expect("store not provided");
let stats = store.global_stats; let stats = store.global_stats;
let initial_theme = if let Some(win) = web_sys::window() { // Use leptos-use for reactive localStorage management
if let Some(doc) = win.document() { let (current_theme, set_current_theme, _) = use_local_storage::<String, FromToStringCodec>("vibetorrent_theme");
doc.document_element()
.and_then(|el| el.get_attribute("data-theme")) // Initialize with default if empty
.unwrap_or_else(|| "dark".to_string()) if current_theme.get_untracked().is_empty() {
} else { set_current_theme.set("dark".to_string());
"dark".to_string()
} }
} else {
"dark".to_string()
};
let (current_theme, set_current_theme) = create_signal(initial_theme);
// Automatically sync theme to document attribute
create_effect(move |_| { create_effect(move |_| {
if let Some(win) = web_sys::window() { let theme = current_theme.get().to_lowercase();
if let Some(storage) = win.local_storage().ok().flatten() { if let Some(doc) = document().document_element() {
if let Ok(Some(stored_theme)) = storage.get_item("vibetorrent_theme") { let _ = doc.set_attribute("data-theme", &theme);
let theme = stored_theme.to_lowercase();
set_current_theme.set(theme.clone());
if let Some(doc) = win.document() {
let _ = doc
.document_element()
.unwrap()
.set_attribute("data-theme", &theme);
}
}
}
} }
}); });
@@ -127,19 +115,20 @@ pub fn StatusBar() -> impl IntoView {
set_active_dropdown.set(0); set_active_dropdown.set(0);
}; };
view! { // Refs for click outside detection
// Transparent overlay to close dropdowns when clicking outside let down_ref = create_node_ref::<html::Div>();
<Show when=move || active_dropdown.get() != 0> let up_ref = create_node_ref::<html::Div>();
<div let theme_ref = create_node_ref::<html::Div>();
class="fixed inset-0 z-[98] cursor-default"
on:pointerdown=move |_| close_all()
></div>
</Show>
let _ = on_click_outside(down_ref, move |_| if active_dropdown.get_untracked() == 1 { close_all() });
let _ = on_click_outside(up_ref, move |_| if active_dropdown.get_untracked() == 2 { close_all() });
let _ = on_click_outside(theme_ref, move |_| if active_dropdown.get_untracked() == 3 { close_all() });
view! {
<div class="fixed bottom-0 left-0 right-0 h-8 min-h-8 bg-base-200 border-t border-base-300 flex items-center px-4 text-xs gap-4 text-base-content/70 z-[99]"> <div class="fixed bottom-0 left-0 right-0 h-8 min-h-8 bg-base-200 border-t border-base-300 flex items-center px-4 text-xs gap-4 text-base-content/70 z-[99]">
// --- DOWNLOAD SPEED DROPDOWN --- // --- DOWNLOAD SPEED DROPDOWN ---
<div class="relative"> <div class="relative" node_ref=down_ref>
<div <div
class="flex items-center gap-2 cursor-pointer hover:text-primary transition-colors select-none" class="flex items-center gap-2 cursor-pointer hover:text-primary transition-colors select-none"
title="Global Download Speed - Click to Set Limit" title="Global Download Speed - Click to Set Limit"
@@ -192,7 +181,7 @@ pub fn StatusBar() -> impl IntoView {
</div> </div>
// --- UPLOAD SPEED DROPDOWN --- // --- UPLOAD SPEED DROPDOWN ---
<div class="relative"> <div class="relative" node_ref=up_ref>
<div <div
class="flex items-center gap-2 cursor-pointer hover:text-primary transition-colors select-none" class="flex items-center gap-2 cursor-pointer hover:text-primary transition-colors select-none"
title="Global Upload Speed - Click to Set Limit" title="Global Upload Speed - Click to Set Limit"
@@ -245,7 +234,7 @@ pub fn StatusBar() -> impl IntoView {
</div> </div>
<div class="ml-auto flex items-center gap-4"> <div class="ml-auto flex items-center gap-4">
<div class="relative"> <div class="relative" node_ref=theme_ref>
<div <div
class="btn btn-ghost btn-xs btn-square" class="btn btn-ghost btn-xs btn-square"
title="Change Theme" title="Change Theme"
@@ -275,14 +264,6 @@ pub fn StatusBar() -> impl IntoView {
on:pointerdown=move |e| { on:pointerdown=move |e| {
e.stop_propagation(); e.stop_propagation();
set_current_theme.set(theme.to_string()); set_current_theme.set(theme.to_string());
if let Some(win) = web_sys::window() {
if let Some(doc) = win.document() {
let _ = doc.document_element().unwrap().set_attribute("data-theme", theme);
}
if let Some(storage) = win.local_storage().ok().flatten() {
let _ = storage.set_item("vibetorrent_theme", theme);
}
}
close_all(); close_all();
} }
> >