From 5d2c7249eb9e846cf3b17a57923ce6ae49fd0cfd Mon Sep 17 00:00:00 2001 From: spinline Date: Sun, 1 Feb 2026 14:08:25 +0300 Subject: [PATCH] feat: modernize UI, add mobile card layout, and fix iOS sidebar interactions --- frontend/public/tailwind.css | 90 +++++++++++++++++++++++ frontend/src/components/layout/sidebar.rs | 20 +++-- frontend/src/components/layout/toolbar.rs | 27 ++++++- frontend/src/components/torrent/table.rs | 16 +++- frontend/src/store.rs | 4 +- 5 files changed, 147 insertions(+), 10 deletions(-) diff --git a/frontend/public/tailwind.css b/frontend/public/tailwind.css index 8846232..206ed59 100644 --- a/frontend/public/tailwind.css +++ b/frontend/public/tailwind.css @@ -1374,6 +1374,15 @@ } } } + .join-item { + &:where(*:not(:first-child, :disabled, [disabled], .btn-disabled)) { + margin-inline-start: calc(var(--border, 1px) * -1); + margin-block-start: 0; + } + &:where(*:is(:disabled, [disabled], .btn-disabled)) { + border-width: var(--border, 1px) 0 var(--border, 1px) var(--border, 1px); + } + } .modal-action { @layer daisyui.l1.l2.l3 { margin-top: calc(0.25rem * 6); @@ -1479,6 +1488,68 @@ } } } + .join { + display: inline-flex; + align-items: stretch; + --join-ss: 0; + --join-se: 0; + --join-es: 0; + --join-ee: 0; + :where(.join-item) { + border-start-start-radius: var(--join-ss, 0); + border-start-end-radius: var(--join-se, 0); + border-end-start-radius: var(--join-es, 0); + border-end-end-radius: var(--join-ee, 0); + * { + --join-ss: var(--radius-field); + --join-se: var(--radius-field); + --join-es: var(--radius-field); + --join-ee: var(--radius-field); + } + } + > .join-item:where(:first-child) { + --join-ss: var(--radius-field); + --join-se: 0; + --join-es: var(--radius-field); + --join-ee: 0; + } + :first-child:not(:last-child) { + :where(.join-item) { + --join-ss: var(--radius-field); + --join-se: 0; + --join-es: var(--radius-field); + --join-ee: 0; + } + } + > .join-item:where(:last-child) { + --join-ss: 0; + --join-se: var(--radius-field); + --join-es: 0; + --join-ee: var(--radius-field); + } + :last-child:not(:first-child) { + :where(.join-item) { + --join-ss: 0; + --join-se: var(--radius-field); + --join-es: 0; + --join-ee: var(--radius-field); + } + } + > .join-item:where(:only-child) { + --join-ss: var(--radius-field); + --join-se: var(--radius-field); + --join-es: var(--radius-field); + --join-ee: var(--radius-field); + } + :only-child { + :where(.join-item) { + --join-ss: var(--radius-field); + --join-se: var(--radius-field); + --join-es: var(--radius-field); + --join-ee: var(--radius-field); + } + } + } .line-clamp-2 { overflow: hidden; display: -webkit-box; @@ -1701,6 +1772,10 @@ border-left-style: var(--tw-border-style); border-left-width: 1px; } + .border-l-0 { + border-left-style: var(--tw-border-style); + border-left-width: 0px; + } .badge-ghost { @layer daisyui.l1.l2 { border-color: var(--color-base-200); @@ -1735,6 +1810,12 @@ .border-base-300 { border-color: var(--color-base-300); } + .border-base-content\/20 { + border-color: var(--color-base-content); + @supports (color: color-mix(in lab, red, red)) { + border-color: color-mix(in oklab, var(--color-base-content) 20%, transparent); + } + } .border-white\/5 { border-color: color-mix(in srgb, #fff 5%, transparent); @supports (color: color-mix(in lab, red, red)) { @@ -1816,6 +1897,9 @@ padding-inline: calc(0.25rem * 2 - var(--border)); } } + .px-2 { + padding-inline: calc(var(--spacing) * 2); + } .px-4 { padding-inline: calc(var(--spacing) * 4); } @@ -2128,6 +2212,12 @@ } } } + .focus\:outline-none { + &:focus { + --tw-outline-style: none; + outline-style: none; + } + } .focus-visible\:ring-2 { &:focus-visible { --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor); diff --git a/frontend/src/components/layout/sidebar.rs b/frontend/src/components/layout/sidebar.rs index 01e5463..142c462 100644 --- a/frontend/src/components/layout/sidebar.rs +++ b/frontend/src/components/layout/sidebar.rs @@ -1,4 +1,5 @@ use leptos::*; +use leptos::wasm_bindgen::JsCast; #[component] pub fn Sidebar() -> impl IntoView { @@ -10,8 +11,17 @@ pub fn Sidebar() -> impl IntoView { let completed_count = move || store.torrents.get().iter().filter(|t| t.status == shared::TorrentStatus::Seeding || t.status == shared::TorrentStatus::Paused).count(); let inactive_count = move || store.torrents.get().iter().filter(|t| t.status == shared::TorrentStatus::Paused || t.status == shared::TorrentStatus::Error).count(); + let close_drawer = move || { + if let Some(element) = document().get_element_by_id("my-drawer") { + if let Ok(input) = element.dyn_into::() { + input.set_checked(false); + } + } + }; + let set_filter = move |f: crate::store::FilterStatus| { store.filter.set(f); + close_drawer(); }; let filter_class = move |f: crate::store::FilterStatus| { @@ -24,7 +34,7 @@ pub fn Sidebar() -> impl IntoView {