diff options
Diffstat (limited to '')
| -rw-r--r-- | src/components/sidebar.rs | 231 |
1 files changed, 144 insertions, 87 deletions
diff --git a/src/components/sidebar.rs b/src/components/sidebar.rs index ca753ef..9f555b5 100644 --- a/src/components/sidebar.rs +++ b/src/components/sidebar.rs @@ -1,20 +1,29 @@ -use std::collections::HashSet; +// SPDX-FileCopyrightText: 2025 cel <cel@bunny.garden> +// +// SPDX-License-Identifier: AGPL-3.0-or-later + +use std::collections::{HashMap, HashSet}; use jid::BareJID; use leptos::prelude::*; +use reactive_stores::Store; use crate::components::{ - personal_status::PersonalStatus, - chats_list::ChatsList, - roster_list::RosterList, + chats_list::ChatsList, personal_status::PersonalStatus, roster_list::RosterList, }; -#[derive(PartialEq, Eq, Clone, Copy)] +#[derive(PartialEq, Eq, Clone, Copy, Hash)] pub enum SidebarOpen { Roster, Chats, } +#[derive(Store)] +pub struct Drawer { + open: SidebarOpen, + hovering: bool, +} + pub enum Open { /// Currently on screen Focused, @@ -55,122 +64,170 @@ pub fn toggle_open(state: &mut Option<SidebarOpen>, open: SidebarOpen) -> bool { } } None => { - *state = Some(open); - true - }, + *state = Some(open); + true + } } } #[component] pub fn Sidebar() -> impl IntoView { - let requests: ReadSignal<HashSet<BareJID>> = use_context().expect("no pending subscriptions in context"); + let requests: ReadSignal<HashSet<BareJID>> = + use_context().expect("no pending subscriptions in context"); // for what has been clicked open (in the background) let (open, set_open) = signal(None::<SidebarOpen>); // for what is just in the hovered state (not clicked to be pinned open yet necessarily) + let open = Memo::new(move |_| open.get()); let (hovered, set_hovered) = signal(None::<SidebarOpen>); + let hovered = Memo::new(move |_| hovered.get()); let (just_closed, set_just_closed) = signal(false); + let just_closed = Memo::new(move |_| just_closed.get()); + + let pages = Memo::new(move |_| { + let mut pages = HashSet::new(); + if let Some(hovered) = *hovered.read() { + pages.insert(hovered); + } + if let Some(opened) = *open.read() { + pages.insert(opened); + } + pages + }); view! { - <div class="sidebar" on:mouseleave=move |_| { - set_hovered.set(None); - set_just_closed.set(false); - }> + <div + class="sidebar" + on:mouseleave=move |_| { + set_hovered.set(None); + set_just_closed.set(false); + } + > <div class="dock panel"> <div class="shortcuts"> - <div class="roster-tab dock-item" class:focused=move || *open.read() == Some(SidebarOpen::Roster) class:hovering=move || *hovered.read() == Some(SidebarOpen::Roster) - on:mouseenter=move |_| { - set_just_closed.set(false); - set_hovered.set(Some(SidebarOpen::Roster)) - } - on:click=move |_| { - set_open.update(|state| { - if !toggle_open(state, SidebarOpen::Roster) { - set_just_closed.set(true); - } - }) - }> + <div + class="roster-tab dock-item" + class:focused=move || *open.read() == Some(SidebarOpen::Roster) + class:hovering=move || *hovered.read() == Some(SidebarOpen::Roster) + on:mouseenter=move |_| { + set_just_closed.set(false); + set_hovered.set(Some(SidebarOpen::Roster)) + } + on:click=move |_| { + set_open + .update(|state| { + if !toggle_open(state, SidebarOpen::Roster) { + set_just_closed.set(true); + } else { + set_just_closed.set(false); + } + }) + } + > <div class="dock-pill"></div> <div class="dock-icon"> <div class="icon-with-badge"> - <img src="/assets/caw.png" /> - {move || { - let len = requests.read().len(); - if len > 0 { - view! { - <div class="badge">{len}</div> - }.into_any() - } else { - view! {}.into_any() - } - }} + <img src="/assets/caw.png" /> + {move || { + let len = requests.read().len(); + if len > 0 { + view! { <div class="badge">{len}</div> }.into_any() + } else { + view! {}.into_any() + } + }} </div> </div> </div> - <div class="chats-tab dock-item" class:focused=move || *open.read() == Some(SidebarOpen::Chats) class:hovering=move || *hovered.read() == Some(SidebarOpen::Chats) - on:mouseenter=move |_| { - set_just_closed.set(false); - set_hovered.set(Some(SidebarOpen::Chats)) - } - on:click=move |_| { - set_open.update(|state| { - if !toggle_open(state, SidebarOpen::Chats) { - set_just_closed.set(true); - } - }) - }> + <div + class="chats-tab dock-item" + class:focused=move || *open.read() == Some(SidebarOpen::Chats) + class:hovering=move || *hovered.read() == Some(SidebarOpen::Chats) + on:mouseenter=move |_| { + set_just_closed.set(false); + set_hovered.set(Some(SidebarOpen::Chats)) + } + on:click=move |_| { + set_open + .update(|state| { + if !toggle_open(state, SidebarOpen::Chats) { + set_just_closed.set(true); + } else { + set_just_closed.set(false); + } + }) + } + > <div class="dock-pill"></div> <img src="/assets/bubble.png" /> </div> </div> - <div class="pins"> - </div> + <div class="pins"></div> <div class="personal"> <PersonalStatus /> </div> </div> - {move || if let Some(hovered) = *hovered.read() { - if Some(hovered) != *open.read() { - if !just_closed.get() { - match hovered { - SidebarOpen::Roster => view! { - <div class="sidebar-drawer sidebar-hovering-drawer"> - <RosterList /> - </div> - }.into_any(), - SidebarOpen::Chats => view! { - <div class="sidebar-drawer sidebar-hovering-drawer"> - <ChatsList /> - </div> - }.into_any(), - } - } else { - - view! {}.into_any() + {move || { + if !just_closed.get() { + view! { + <For each=move || pages.get() key=|page| *page let(page)> + {move || match page { + SidebarOpen::Roster => { + view! { + {move || { + if *open.read() == None + && *hovered.read() == Some(SidebarOpen::Roster) + { + view! { <div class="sidebar-drawer behind-hovering"></div> } + .into_any() + } else { + view! {}.into_any() + } + }} + <div + class:sidebar-drawer=true + class:sidebar-hovering-drawer=move || { + !(*open.read() == Some(SidebarOpen::Roster)) + && (*hovered.read() == Some(SidebarOpen::Roster)) + } + > + <RosterList /> + </div> + } + .into_any() + } + SidebarOpen::Chats => { + view! { + {move || { + if *open.read() == None + && *hovered.read() == Some(SidebarOpen::Chats) + { + view! { <div class="sidebar-drawer behind-hovering"></div> } + .into_any() + } else { + view! {}.into_any() + } + }} + <div + class:sidebar-drawer=true + class:sidebar-hovering-drawer=move || { + !(*open.read() == Some(SidebarOpen::Chats)) + && (*hovered.read() == Some(SidebarOpen::Chats)) + } + > + <ChatsList /> + </div> + } + .into_any() + } + }} + </For> } + .into_any() } else { view! {}.into_any() } - } else { - view! {}.into_any() - }} - {move || if let Some(opened) = *open.read() { - match opened { - SidebarOpen::Roster => view! { - <div class="sidebar-drawer"> - <RosterList /> - </div> - }.into_any(), - SidebarOpen::Chats => view! { - <div class="sidebar-drawer"> - <ChatsList /> - </div> - }.into_any(), - } - } else { - view! {}.into_any() }} </div> } } - |
