Compare commits

..

3 Commits

Author SHA1 Message Date
spinline
93a43d1b38 feat(frontend): implement rust-ui ScrollArea for details tab scrolling
All checks were successful
Build MIPS Binary / build (push) Successful in 2m1s
2026-02-21 20:44:20 +03:00
spinline
91ca6ff96f fix(frontend): adjust flex shrink and min heights for safe mobile scrolling in details tab
All checks were successful
Build MIPS Binary / build (push) Successful in 2m0s
2026-02-21 20:36:39 +03:00
spinline
8c0d35cca5 fix(frontend): force absolute positioning for tabs scroll
All checks were successful
Build MIPS Binary / build (push) Successful in 2m1s
2026-02-21 20:31:49 +03:00
3 changed files with 137 additions and 5 deletions

View File

@@ -51,9 +51,9 @@ pub fn TorrentDetailsSheet() -> impl IntoView {
</SheetClose>
</div>
<div class="flex-1 min-h-0 overflow-hidden p-6 flex flex-col">
<Tabs default_value="general" class="flex-1 min-h-0 flex flex-col">
<TabsList class="w-full justify-start rounded-none border-b bg-transparent p-0">
<div class="flex-1 overflow-hidden flex flex-col pt-4 px-6 pb-0">
<Tabs default_value="general" class="flex-1 h-full min-h-0 flex flex-col">
<TabsList class="w-full justify-start rounded-none border-b bg-transparent p-0 shrink-0">
<TabsTrigger value="general" class="data-[state=active]:bg-transparent data-[state=active]:shadow-none data-[state=active]:border-b-2 data-[state=active]:border-primary rounded-none">
"Genel"
</TabsTrigger>
@@ -68,7 +68,7 @@ pub fn TorrentDetailsSheet() -> impl IntoView {
</TabsTrigger>
</TabsList>
<div class="flex-1 min-h-0 overflow-y-auto mt-4 pb-12 pr-2">
<crate::components::ui::scroll_area::ScrollArea class="flex-1 min-h-0 mt-4 pb-12 pr-4">
<TabsContent value="general" class="space-y-6 animate-in fade-in slide-in-from-bottom-2 duration-300">
<crate::components::ui::shimmer::Shimmer
loading=Signal::derive(move || selected_torrent.get().is_none())
@@ -186,7 +186,7 @@ pub fn TorrentDetailsSheet() -> impl IntoView {
<p class="text-sm font-medium">"Eş listesi yakında eklenecek"</p>
</div>
</TabsContent>
</div>
</crate::components::ui::scroll_area::ScrollArea>
</Tabs>
</div>
</SheetContent>

View File

@@ -14,6 +14,7 @@ pub mod input;
pub mod multi_select;
pub mod select;
pub mod separator;
pub mod scroll_area;
pub mod sheet;
pub mod sidenav;
pub mod skeleton;

View File

@@ -0,0 +1,131 @@
use leptos::prelude::*;
use leptos_ui::void;
use tw_merge::*;
mod components {
use super::*;
void! {ScrollAreaThumb, div, "bg-border relative flex-1 rounded-full"}
void! {ScrollAreaCorner, div, "bg-border"}
}
pub use components::*;
/* ========================================================== */
/* ✨ COMPONENTS ✨ */
/* ========================================================== */
#[component]
pub fn ScrollArea(children: Children, #[prop(into, optional)] class: String) -> impl IntoView {
let merged_class = tw_merge!("relative overflow-hidden", class);
view! {
<div data-name="ScrollArea" class=merged_class>
<ScrollAreaViewport>{children()}</ScrollAreaViewport>
<ScrollBar />
<ScrollAreaCorner />
</div>
}
}
#[component]
pub fn ScrollAreaViewport(children: Children, #[prop(into, optional)] class: String) -> impl IntoView {
let merged_class = tw_merge!(
"focus-visible:ring-ring/50 size-full rounded-[inherit] transition-[color,box-shadow] outline-none focus-visible:ring-[3px] focus-visible:outline-1 overflow-auto",
class
);
view! {
<div data-name="ScrollAreaViewport" class=merged_class>
{children()}
</div>
}
}
/* ========================================================== */
/* 🧬 ENUMS 🧬 */
/* ========================================================== */
#[derive(Clone, Copy, Default)]
pub enum ScrollBarOrientation {
#[default]
Vertical,
Horizontal,
}
#[component]
pub fn ScrollBar(
#[prop(default = ScrollBarOrientation::default())] orientation: ScrollBarOrientation,
#[prop(into, optional)] class: String,
) -> impl IntoView {
let orientation_class = match orientation {
ScrollBarOrientation::Vertical => "h-full w-2.5 border-l border-l-transparent",
ScrollBarOrientation::Horizontal => "h-2.5 flex-col border-t border-t-transparent",
};
let merged_class = tw_merge!("flex touch-none p-px transition-colors select-none", orientation_class, class);
view! {
<div data-name="ScrollBar" class=merged_class>
<ScrollAreaThumb />
</div>
}
}
/* ========================================================== */
/* 🧬 STRUCT 🧬 */
/* ========================================================== */
#[component]
pub fn SnapScrollArea(
#[prop(into, default = SnapAreaVariant::default())] variant: SnapAreaVariant,
#[prop(into, optional)] class: String,
children: Children,
) -> impl IntoView {
let snap_item = SnapAreaClass { variant };
let merged_class = snap_item.with_class(class);
view! {
<div data-name="SnapScrollArea" class=merged_class>
{children()}
</div>
}
}
#[derive(TwClass, Default)]
#[tw(class = "")]
pub struct SnapAreaClass {
variant: SnapAreaVariant,
}
#[derive(TwVariant)]
pub enum SnapAreaVariant {
// * snap-x by default
#[tw(default, class = "overflow-x-auto snap-x")]
Center,
}
/* ========================================================== */
/* 🧬 STRUCT 🧬 */
/* ========================================================== */
#[component]
pub fn SnapItem(
#[prop(into, default = SnapVariant::default())] variant: SnapVariant,
#[prop(into, optional)] class: String,
children: Children,
) -> impl IntoView {
let snap_item = SnapItemClass { variant };
let merged_class = snap_item.with_class(class);
view! {
<div data-name="SnapItem" class=merged_class>
{children()}
</div>
}
}
#[derive(TwClass, Default)]
#[tw(class = "shrink-0")]
pub struct SnapItemClass {
variant: SnapVariant,
}
#[derive(TwVariant)]
pub enum SnapVariant {
// * snap-center by default
#[tw(default, class = "snap-center")]
Center,
}