Compare commits

..

14 Commits

Author SHA1 Message Date
spinline
8fc3571848 fix: restore missing BASE64 import
All checks were successful
Build MIPS Binary / build (push) Successful in 1m51s
2026-02-14 02:23:08 +03:00
spinline
792b6bc97b fix: resolve toolbar syntax error and unused imports
Some checks failed
Build MIPS Binary / build (push) Failing after 34s
2026-02-14 02:20:53 +03:00
spinline
6efa6cd2d9 fix: resolve unused variable warning
Some checks failed
Build MIPS Binary / build (push) Failing after 45s
2026-02-14 02:18:45 +03:00
spinline
89caa17b92 fix: correct malformed import in multi_select.rs
Some checks failed
Build MIPS Binary / build (push) Failing after 46s
2026-02-14 02:16:53 +03:00
spinline
47bb60d7d8 fix: explicit type fixes for toolbar and multi_select
Some checks failed
Build MIPS Binary / build (push) Failing after 45s
2026-02-14 02:14:40 +03:00
spinline
7730250b61 fix: resolve updated compilation errors
Some checks failed
Build MIPS Binary / build (push) Failing after 45s
2026-02-14 02:11:58 +03:00
spinline
73d111124a refactor: simplify toolbar toggle button structure
Some checks failed
Build MIPS Binary / build (push) Failing after 32s
2026-02-14 02:10:43 +03:00
spinline
670c5a653b fix: resolve type inference error in toolbar callback
Some checks failed
Build MIPS Binary / build (push) Failing after 40s
2026-02-14 02:09:12 +03:00
spinline
9394a56e7d fix: compile error in protected layout
Some checks failed
Build MIPS Binary / build (push) Failing after 45s
2026-02-14 02:07:43 +03:00
spinline
105388eec3 feat: refine responsive sidebar behavior with auto-collapse
Some checks failed
Build MIPS Binary / build (push) Failing after 30s
2026-02-14 02:06:20 +03:00
spinline
f5d9cb642c feat: implement collapsible sidebar
Some checks failed
Build MIPS Binary / build (push) Failing after 34s
2026-02-14 01:57:40 +03:00
spinline
3ce980239c fix: ensure context menu closes after triggering start or stop actions
All checks were successful
Build MIPS Binary / build (push) Successful in 1m51s
2026-02-14 01:25:47 +03:00
spinline
d00fc41010 style: update CSS configuration and theme variables
All checks were successful
Build MIPS Binary / build (push) Successful in 1m55s
2026-02-13 20:11:02 +03:00
spinline
0636020a86 chore: re-add Leptos metadata to Cargo.toml for ui-cli compatibility
All checks were successful
Build MIPS Binary / build (push) Successful in 1m52s
2026-02-13 20:03:01 +03:00
23 changed files with 579 additions and 1231 deletions

View File

@@ -1,4 +1,3 @@
mod components;
mod diff;
mod handlers;
#[cfg(feature = "push-notifications")]

View File

@@ -20,7 +20,7 @@
<link rel="apple-touch-icon" sizes="512x512" href="icon-512.png" />
<!-- Trunk Assets -->
<link data-trunk rel="rust" href="Cargo.toml" data-wasm-opt="0" data-preload="false" />
<script data-trunk rel="rust" src="Cargo.toml" data-wasm-opt="0" data-preload="false"></script>
<link data-trunk rel="css" href="public/tailwind.css" />
<link data-trunk rel="copy-file" href="manifest.json" />
<link data-trunk rel="copy-file" href="icon-192.png" />

View File

@@ -1,6 +1,7 @@
@import "tailwindcss";
@import "tw-animate-css";
:root {
--radius: 0.625rem;
--background: oklch(1 0 0);
@@ -44,6 +45,7 @@
--ring: oklch(0.556 0 0);
}
@theme inline {
--color-background: var(--background);
--color-foreground: var(--foreground);
@@ -80,10 +82,10 @@
button:not(:disabled),
[role="button"]:not(:disabled) {
@apply cursor-pointer;
cursor: pointer;
}
dialog {
@apply m-auto;
margin: auto;
}
}

View File

@@ -1,34 +1,24 @@
use leptos::prelude::*;
use leptos::task::spawn_local;
use crate::components::ui::card::{Card, CardHeader, CardContent};
use crate::components::ui::auto_form::{AutoForm, AutoFormField};
use crate::components::ui::input::{Input, InputType};
use crate::components::ui::button::Button;
#[component]
pub fn Login() -> impl IntoView {
let username = RwSignal::new(String::new());
let password = RwSignal::new(String::new());
let error = signal(Option::<String>::None);
let loading = signal(false);
let fields = vec![
AutoFormField::Text {
name: "username".to_string(),
label: "Kullanıcı Adı".to_string(),
placeholder: Some("Kullanıcı adınız".to_string()),
required: true,
},
AutoFormField::Password {
name: "password".to_string(),
label: "Şifre".to_string(),
placeholder: Some("******".to_string()),
required: true,
},
];
let on_submit = move |data: std::collections::HashMap<String, String>| {
let handle_login = move |ev: web_sys::SubmitEvent| {
ev.prevent_default();
loading.1.set(true);
error.1.set(None);
let user = data.get("username").cloned().unwrap_or_default();
let pass = data.get("password").cloned().unwrap_or_default();
let user = username.get();
let pass = password.get();
spawn_local(async move {
match shared::server_fns::auth::login(user, pass).await {
@@ -59,20 +49,47 @@ pub fn Login() -> impl IntoView {
</CardHeader>
<CardContent class="pt-4">
<AutoForm
fields=fields
submit_label="Giriş Yap"
on_submit=on_submit
loading=loading.0.into()
/>
<Show when=move || error.0.get().is_some()>
<div class="mt-4 rounded-lg border border-destructive/50 bg-destructive/10 p-3 text-sm text-destructive">
{move || error.0.get().unwrap_or_default()}
<form on:submit=handle_login class="space-y-4">
<div class="space-y-2">
<label class="text-sm font-medium leading-none">"Kullanıcı Adı"</label>
<Input
r#type=InputType::Text
placeholder="Kullanıcı adınız"
bind_value=username
disabled=loading.0.get()
/>
</div>
</Show>
<div class="space-y-2">
<label class="text-sm font-medium leading-none">"Şifre"</label>
<Input
r#type=InputType::Password
placeholder="******"
bind_value=password
disabled=loading.0.get()
/>
</div>
<Show when=move || error.0.get().is_some()>
<div class="rounded-lg border border-destructive/50 bg-destructive/10 p-3 text-sm text-destructive">
{move || error.0.get().unwrap_or_default()}
</div>
</Show>
<div class="pt-2">
<Button
class="w-full"
attr:r#type="submit"
attr:disabled=move || loading.0.get()
>
<Show when=move || loading.0.get() fallback=|| view! { "Giriş Yap" }.into_any()>
<span class="animate-spin mr-2 h-4 w-4 border-2 border-current border-t-transparent rounded-full"></span>
"Giriş Yapılıyor..."
</Show>
</Button>
</div>
</form>
</CardContent>
</Card>
</div>
}
}
}

View File

@@ -1,38 +1,23 @@
use leptos::prelude::*;
use leptos::task::spawn_local;
use crate::components::ui::card::{Card, CardHeader, CardContent};
use crate::components::ui::auto_form::{AutoForm, AutoFormField};
use crate::components::ui::input::{Input, InputType};
use crate::components::ui::button::Button;
#[component]
pub fn Setup() -> impl IntoView {
let username = RwSignal::new(String::new());
let password = RwSignal::new(String::new());
let confirm_password = RwSignal::new(String::new());
let error = signal(Option::<String>::None);
let loading = signal(false);
let fields = vec![
AutoFormField::Text {
name: "username".to_string(),
label: "Yönetici Kullanıcı Adı".to_string(),
placeholder: Some("admin".to_string()),
required: true,
},
AutoFormField::Password {
name: "password".to_string(),
label: "Şifre".to_string(),
placeholder: Some("******".to_string()),
required: true,
},
AutoFormField::Password {
name: "confirm_password".to_string(),
label: "Şifre Onay".to_string(),
placeholder: Some("******".to_string()),
required: true,
},
];
let on_submit = move |data: std::collections::HashMap<String, String>| {
let user = data.get("username").cloned().unwrap_or_default();
let pass = data.get("password").cloned().unwrap_or_default();
let confirm = data.get("confirm_password").cloned().unwrap_or_default();
let handle_setup = move |ev: web_sys::SubmitEvent| {
ev.prevent_default();
let pass = password.get();
let confirm = confirm_password.get();
if pass != confirm {
error.1.set(Some("Şifreler eşleşmiyor".to_string()));
@@ -47,6 +32,8 @@ pub fn Setup() -> impl IntoView {
loading.1.set(true);
error.1.set(None);
let user = username.get();
spawn_local(async move {
match shared::server_fns::auth::setup(user, pass).await {
Ok(_) => {
@@ -77,18 +64,54 @@ pub fn Setup() -> impl IntoView {
</CardHeader>
<CardContent class="pt-4">
<AutoForm
fields=fields
submit_label="Kurulumu Tamamla"
on_submit=on_submit
loading=loading.0.into()
/>
<Show when=move || error.0.get().is_some() fallback=|| ()>
<div class="mt-4 rounded-lg border border-destructive/50 bg-destructive/10 p-3 text-sm text-destructive">
<span>{move || error.0.get().unwrap_or_default()}</span>
<form on:submit=handle_setup class="space-y-4">
<div class="space-y-2">
<label class="text-sm font-medium leading-none">"Yönetici Kullanıcı Adı"</label>
<Input
r#type=InputType::Text
placeholder="admin"
bind_value=username
disabled=loading.0.get()
/>
</div>
</Show>
<div class="space-y-2">
<label class="text-sm font-medium leading-none">"Şifre"</label>
<Input
r#type=InputType::Password
placeholder="******"
bind_value=password
disabled=loading.0.get()
/>
</div>
<div class="space-y-2">
<label class="text-sm font-medium leading-none">"Şifre Onay"</label>
<Input
r#type=InputType::Password
placeholder="******"
bind_value=confirm_password
disabled=loading.0.get()
/>
</div>
<Show when=move || error.0.get().is_some() fallback=|| ()>
<div class="rounded-lg border border-destructive/50 bg-destructive/10 p-3 text-sm text-destructive">
<span>{move || error.0.get().unwrap_or_default()}</span>
</div>
</Show>
<div class="pt-2">
<Button
class="w-full"
attr:r#type="submit"
attr:disabled=move || loading.0.get()
>
<Show when=move || loading.0.get() fallback=|| view! { "Kurulumu Tamamla" }.into_any()>
<span class="animate-spin mr-2 h-4 w-4 border-2 border-current border-t-transparent rounded-full"></span>
"Kuruluyor..."
</Show>
</Button>
</div>
</form>
</CardContent>
</Card>
</div>

View File

@@ -24,10 +24,16 @@ pub fn TorrentContextMenu(
{children()}
</ContextMenuTrigger>
<ContextMenuContent class="w-56 p-1.5">
<ContextMenuItem on:click={let h = hash_c1; move |_| on_action_stored.get_value().run(("start".to_string(), h.clone()))}>
<ContextMenuItem on:click={let h = hash_c1; move |_| {
on_action_stored.get_value().run(("start".to_string(), h.clone()));
crate::components::ui::context_menu::close_context_menu();
}}>
"Başlat"
</ContextMenuItem>
<ContextMenuItem on:click={let h = hash_c2; move |_| on_action_stored.get_value().run(("stop".to_string(), h.clone()))}>
<ContextMenuItem on:click={let h = hash_c2; move |_| {
on_action_stored.get_value().run(("stop".to_string(), h.clone()));
crate::components::ui::context_menu::close_context_menu();
}}>
"Durdur"
</ContextMenuItem>

View File

@@ -1,4 +1,4 @@
use leptos::prelude::*;
// use leptos::prelude::*;
pub fn use_random_id_for(prefix: &str) -> String {
format!("{}_{}", prefix, js_sys::Math::random().to_string().replace(".", ""))

View File

@@ -3,20 +3,53 @@ use crate::components::layout::sidebar::Sidebar;
use crate::components::layout::toolbar::Toolbar;
use crate::components::layout::footer::Footer;
use crate::components::ui::sidenav::{SidenavWrapper, Sidenav, SidenavInset};
use wasm_bindgen::JsCast;
#[component]
pub fn Protected(children: Children) -> impl IntoView {
let (collapsed, set_collapsed) = signal(false);
// Responsive Sidebar Logic
Effect::new(move |_| {
let window = web_sys::window().expect("window missing");
// Initial check
let width = window.inner_width().unwrap().as_f64().unwrap_or(1920.0);
if width < 1280.0 {
set_collapsed.set(true);
} else {
set_collapsed.set(false);
}
// Listener
let closure = wasm_bindgen::closure::Closure::<dyn FnMut(_)>::new(move |_: web_sys::Event| {
let window = web_sys::window().expect("window missing");
let width = window.inner_width().unwrap().as_f64().unwrap_or(1920.0);
if width < 1280.0 {
set_collapsed.set(true);
} else {
set_collapsed.set(false);
}
});
let _ = window.add_event_listener_with_callback("resize", closure.as_ref().unchecked_ref());
closure.forget(); // Leak memory intentionally for global listener (or store in a cleanup handle if needed, but for layout component it's fine)
});
view! {
<SidenavWrapper attr:style="--sidenav-width:16rem; --sidenav-width-icon:3rem;">
// Masaüstü Sidenav
<Sidenav>
<Sidenav
data_collapsible=crate::components::ui::sidenav::SidenavCollapsible::Icon
data_state=if collapsed.get() { crate::components::ui::sidenav::SidenavState::Collapsed } else { crate::components::ui::sidenav::SidenavState::Expanded }
>
<Sidebar />
</Sidenav>
// İçerik Alanı
<SidenavInset class="flex flex-col h-screen overflow-hidden">
// Toolbar (Üst Bar)
<Toolbar />
<Toolbar on_toggle_sidebar=Callback::new(move |_| set_collapsed.update(|c| *c = !*c)) />
// Ana İçerik
<main class="flex-1 overflow-y-auto relative bg-background flex flex-col">

View File

@@ -87,7 +87,7 @@ pub fn Sidebar() -> impl IntoView {
<path stroke-linecap="round" stroke-linejoin="round" d="M15.362 5.214A8.252 8.252 0 0112 21 8.25 8.25 0 016.038 7.048 8.287 8.287 0 009 9.6a8.983 8.983 0 013.361-6.867 8.21 8.25 0 003 2.48z" />
</svg>
</div>
<div class="grid flex-1 text-left text-sm leading-tight overflow-hidden">
<div class="grid flex-1 text-left text-sm leading-tight overflow-hidden group-data-[state=Collapsed]:hidden">
<span class="truncate font-semibold text-foreground text-base">"VibeTorrent"</span>
<span class="truncate text-[10px] text-muted-foreground opacity-70">"v3.0.0"</span>
</div>
@@ -150,26 +150,28 @@ pub fn Sidebar() -> impl IntoView {
<div class="flex flex-col gap-4 p-4">
// Push Notification Toggle
<div class="flex items-center justify-between px-2 py-1 bg-muted/20 rounded-md border border-border/50">
<div class="flex flex-col gap-0.5">
<div class="flex flex-col gap-0.5 group-data-[state=Collapsed]:hidden">
<span class="text-[10px] font-bold uppercase tracking-wider text-foreground/70">"Bildirimler"</span>
<span class="text-[9px] text-muted-foreground">"Web Push"</span>
</div>
<Switch
checked=Signal::from(store.push_enabled)
on_checked_change=Callback::new(on_push_toggle)
/>
<div class="group-data-[state=Collapsed]:hidden">
<Switch
checked=Signal::from(store.push_enabled)
on_checked_change=Callback::new(on_push_toggle)
/>
</div>
</div>
<div class="flex items-center gap-3 p-2 rounded-lg border bg-muted/30 shadow-xs overflow-hidden">
<div class="flex items-center gap-3 p-2 rounded-lg border bg-muted/30 shadow-xs overflow-hidden group-data-[state=Collapsed]:gap-0 group-data-[state=Collapsed]:justify-center group-data-[state=Collapsed]:p-0 group-data-[state=Collapsed]:border-none group-data-[state=Collapsed]:bg-transparent group-data-[state=Collapsed]:shadow-none">
<div class="h-8 w-8 rounded-full bg-primary text-primary-foreground flex items-center justify-center text-xs font-medium shrink-0 border border-primary-foreground/10">
{first_letter}
</div>
<div class="flex-1 overflow-hidden">
<div class="flex-1 overflow-hidden group-data-[state=Collapsed]:hidden">
<div class="font-medium text-[11px] truncate text-foreground leading-tight">{username}</div>
<div class="text-[9px] text-muted-foreground truncate opacity-70">"Yönetici"</div>
</div>
<div class="flex items-center gap-1">
<div class="flex items-center gap-1 group-data-[state=Collapsed]:hidden">
<ThemeToggle />
<Button
@@ -217,8 +219,8 @@ fn SidebarItem(
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-4 shrink-0">
<path stroke-linecap="round" stroke-linejoin="round" d=icon.clone() />
</svg>
<span class="flex-1 truncate">{label}</span>
<span class="text-[10px] font-mono opacity-50">{count}</span>
<span class="flex-1 truncate group-data-[state=Collapsed]:hidden">{label}</span>
<span class="text-[10px] font-mono opacity-50 group-data-[state=Collapsed]:hidden">{count}</span>
</SidenavMenuButton>
</SidenavMenuItem>
}

View File

@@ -1,24 +1,38 @@
use leptos::prelude::*;
use icons::{PanelLeft, Plus};
use crate::components::torrent::add_torrent::AddTorrentDialogContent;
use crate::components::ui::button::{ButtonVariant, ButtonSize};
use crate::components::ui::button::{Button, ButtonVariant, ButtonSize};
use crate::components::ui::sheet::{Sheet, SheetContent, SheetTrigger, SheetDirection};
use crate::components::ui::dialog::{Dialog, DialogContent, DialogTrigger};
use crate::components::layout::sidebar::Sidebar;
#[component]
pub fn Toolbar() -> impl IntoView {
pub fn Toolbar(
on_toggle_sidebar: Callback<()>,
) -> impl IntoView {
view! {
<div class="flex min-h-14 h-auto items-center border-b border-border bg-background px-4" style="padding-top: env(safe-area-inset-top);">
// Sol kısım: Menü butonu (Mobil) + Add Torrent
<div class="flex items-center gap-3">
// --- MOBILE SHEET (SIDEBAR) ---
// Desktop Toggle
<div class="hidden lg:block">
<Button
variant=ButtonVariant::Ghost
size=ButtonSize::Icon
class="size-9"
on:click=move |_| { on_toggle_sidebar.run(()); }
>
<PanelLeft class="size-5" />
<span class="hidden">"Toggle Sidebar"</span>
</Button>
</div>
// Mobile Toggle (Sheet)
<div class="lg:hidden">
<Sheet>
<SheetTrigger variant=ButtonVariant::Ghost size=ButtonSize::Icon class="size-9">
<PanelLeft class="size-5" />
<span class="hidden">"Menüyü Aç"</span>
<span class="hidden">"Open Menu"</span>
</SheetTrigger>
<SheetContent
direction=SheetDirection::Left

View File

@@ -1,93 +0,0 @@
use leptos::prelude::*;
use tailwind_fuse::tw_merge;
use crate::components::ui::button::Button;
use crate::components::ui::input::{Input, InputType};
#[derive(Clone, Debug)]
pub enum AutoFormField {
Text {
name: String,
label: String,
placeholder: Option<String>,
required: bool,
},
Password {
name: String,
label: String,
placeholder: Option<String>,
required: bool,
},
}
#[component]
pub fn AutoForm(
#[prop(into)] fields: Vec<AutoFormField>,
#[prop(into)] submit_label: String,
#[prop(into)] on_submit: Callback<std::collections::HashMap<String, String>>,
#[prop(optional)] loading: Signal<bool>,
#[prop(optional, into)] class: String,
) -> impl IntoView {
let field_values = fields.iter().map(|f| {
let name = match f {
AutoFormField::Text { name, .. } => name,
AutoFormField::Password { name, .. } => name,
};
(name.clone(), RwSignal::new(String::new()))
}).collect::<std::collections::HashMap<String, RwSignal<String>>>();
let handle_submit = {
let field_values = field_values.clone();
move |ev: web_sys::SubmitEvent| {
ev.prevent_default();
let mut data = std::collections::HashMap::new();
for (name, signal) in &field_values {
data.insert(name.clone(), signal.get());
}
on_submit.run(data);
}
};
view! {
<form on:submit=handle_submit class=tw_merge!("space-y-4", class)>
{fields.into_iter().map(|field| {
let (name, label, placeholder, r#type, required) = match field {
AutoFormField::Text { name, label, placeholder, required } => (name, label, placeholder, InputType::Text, required),
AutoFormField::Password { name, label, placeholder, required } => (name, label, placeholder, InputType::Password, required),
};
let signal = field_values.get(&name).cloned().unwrap();
view! {
<div class="space-y-2">
<label class="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70">
{label}
</label>
<Input
r#type=r#type
placeholder=placeholder.unwrap_or_default()
bind_value=signal
required=required
disabled=loading.get()
/>
</div>
}
}).collect_view()}
<div class="pt-2">
<Button
class="w-full"
attr:r#type="submit"
attr:disabled=move || loading.get()
>
<Show when=move || loading.get() fallback=move || {
let label = submit_label.clone();
view! { <span>{label}</span> }.into_any()
}>
<span class="animate-spin mr-2 h-4 w-4 border-2 border-current border-t-transparent rounded-full"></span>
"İşleniyor..."
</Show>
</Button>
</div>
</form>
}
}

View File

@@ -139,7 +139,7 @@ pub fn ContextMenuTrigger(
class=trigger_class
data-name="ContextMenuTrigger"
data-context-trigger=ctx.target_id
on:contextmenu=move |e: web_sys::MouseEvent| {
on:contextmenu=move |_e: web_sys::MouseEvent| {
if let Some(cb) = on_open {
cb.run(());
}

View File

@@ -1,6 +1,6 @@
// * Reuse @table.rs
pub use crate::components::ui::table::{
Table as DataTable, TableBody as DataTableBody, TableCaption as DataTableCaption, TableCell as DataTableCell,
TableFooter as DataTableFooter, TableHead as DataTableHead, TableHeader as DataTableHeader,
Table as DataTable, TableBody as DataTableBody, TableCell as DataTableCell,
TableHead as DataTableHead, TableHeader as DataTableHeader,
TableRow as DataTableRow, TableWrapper as DataTableWrapper,
};

View File

@@ -5,7 +5,7 @@ use leptos_ui::clx;
use tw_merge::*;
use crate::components::hooks::use_random::use_random_id_for;
pub use crate::components::ui::separator::Separator as DropdownMenuSeparator;
// pub use crate::components::ui::separator::Separator as DropdownMenuSeparator;
mod components {
use super::*;

View File

@@ -1,6 +1,5 @@
pub mod accordion;
pub mod alert_dialog;
pub mod auto_form;
pub mod badge;
pub mod button;
pub mod button_action;

View File

@@ -9,7 +9,7 @@ use crate::components::hooks::use_can_scroll_vertical::use_can_scroll_vertical;
use crate::components::hooks::use_random::use_random_id_for;
// * Reuse @select.rs
pub use crate::components::ui::select::{
SelectGroup as MultiSelectGroup, SelectItem as MultiSelectItem, SelectLabel as MultiSelectLabel,
SelectGroup as MultiSelectGroup, SelectItem as MultiSelectItem,
};
#[derive(Clone, Copy, PartialEq, Eq, Default)]

View File

@@ -1,26 +0,0 @@
use leptos::prelude::*;
use tailwind_fuse::tw_merge;
#[component]
pub fn Progress(
#[prop(into)] value: Signal<f64>,
#[prop(optional, into)] class: String,
) -> impl IntoView {
let progress_style = move || format!("transform: translateX(-{}%);", 100.0 - value.get().clamp(0.0, 100.0));
view! {
<div
data-name="Progress"
class=tw_merge!(
"relative h-2 w-full overflow-hidden rounded-full bg-primary/20",
class
)
>
<div
data-name="ProgressIndicator"
class="h-full w-full flex-1 bg-primary transition-all duration-500 ease-in-out"
style=progress_style
/>
</div>
}
}

View File

@@ -16,7 +16,7 @@ mod components {
clx! {SheetFooter, footer, "flex flex-col-reverse gap-2 sm:flex-row sm:justify-end"}
}
pub use components::*;
// pub use components::*;
/* ========================================================== */
/* ✨ CONTEXT ✨ */

View File

@@ -145,7 +145,8 @@ pub fn Sidenav(
data-name="Sidenav"
data-sidenav=data_state.to_string()
data-side=data_side.to_string()
class="hidden md:block group peer text-sidenav-foreground data-[state=Collapsed]:hidden"
data-collapsible=data_collapsible.to_string()
class="hidden md:block group peer text-sidenav-foreground group-data-[collapsible=Offcanvas]:data-[state=Collapsed]:hidden"
>
// * SidenavGap: This is what handles the sidenav gap on desktop
<div
@@ -155,9 +156,9 @@ pub fn Sidenav(
"group-data-[collapsible=Offcanvas]:w-0",
"group-data-[side=Right]:rotate-180",
match variant {
SidenavVariant::Sidenav => "group-data-[collapsible=Icon]:w-(--sidenav-width-icon)",
SidenavVariant::Sidenav => "group-data-[collapsible=Icon]:group-data-[state=Collapsed]:w-(--sidenav-width-icon)",
SidenavVariant::Floating | SidenavVariant::Inset =>
"group-data-[collapsible=Icon]:w-[calc(var(--sidenav-width-icon)+(--spacing(4)))]",
"group-data-[collapsible=Icon]:group-data-[state=Collapsed]:w-[calc(var(--sidenav-width-icon)+(--spacing(4)))]",
}
)
/>
@@ -171,9 +172,9 @@ pub fn Sidenav(
SidenavSide::Right => "right-0 group-data-[collapsible=Offcanvas]:right-[calc(var(--sidenav-width)*-1)]"
},
match variant {
SidenavVariant::Sidenav => "group-data-[collapsible=Icon]:w-(--sidenav-width-icon) group-data-[side=Left]:border-r group-data-[side=Right]:border-l",
SidenavVariant::Sidenav => "group-data-[collapsible=Icon]:group-data-[state=Collapsed]:w-(--sidenav-width-icon) group-data-[side=Left]:border-r group-data-[side=Right]:border-l",
SidenavVariant::Floating | SidenavVariant::Inset =>
"p-2 group-data-[collapsible=Icon]:w-[calc(var(--sidenav-width-icon)+(--spacing(4))+2px)]",
"p-2 group-data-[collapsible=Icon]:group-data-[state=Collapsed]:w-[calc(var(--sidenav-width-icon)+(--spacing(4))+2px)]",
},
)
>

View File

@@ -192,7 +192,7 @@ pub async fn subscribe_to_push_notifications() {
let key_array = js_sys::Uint8Array::from(&decoded_key[..]);
// 3. Prepare Options
let mut options = web_sys::PushSubscriptionOptionsInit::new();
let options = web_sys::PushSubscriptionOptionsInit::new();
options.set_user_visible_only(true);
options.set_application_server_key(&key_array.into());

1369
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,8 +1,8 @@
{
"dependencies": {
"@tailwindcss/cli": "^4.1.18",
"tailwindcss": "^4.1.18",
"tw-animate-css": "^1.4.0"
},
"type": "module"
}
"type": "module",
"dependencies": {
"@tailwindcss/cli": "^4.1.18",
"tailwindcss": "^4.1.18",
"tw-animate-css": "^1.4.0"
}
}