diff --git a/AobaClient/assets/style/main.scss b/AobaClient/assets/style/main.scss index adf5d3c..0c452ae 100644 --- a/AobaClient/assets/style/main.scss +++ b/AobaClient/assets/style/main.scss @@ -161,6 +161,7 @@ form { .contextMenu { backdrop-filter: blur(10px) brightness(0.5) grayscale(1); position: absolute; + z-index: 100; .itemList { display: flex; diff --git a/AobaClient/src/components/context_menu.rs b/AobaClient/src/components/context_menu.rs index ad0fedd..7c1738b 100644 --- a/AobaClient/src/components/context_menu.rs +++ b/AobaClient/src/components/context_menu.rs @@ -2,15 +2,58 @@ use core::str; use dioxus::prelude::*; -#[derive(PartialEq, Clone, Props)] -pub struct ContextMenuProps { - pub top: f64, - pub left: f64, - pub items: Option>, +pub mod props { + use dioxus::prelude::*; + + #[derive(PartialEq, Clone, Props)] + pub struct ContextMenu { + pub top: f64, + pub left: f64, + pub items: Option>, + } + + #[derive(PartialEq, Clone, Props, Default)] + pub struct ContextMenuItem { + pub name: String, + pub sub_items: Option>, + } +} + +#[derive(Clone, Copy, Default)] +pub struct ContextMenuRenderer { + pub menu: Signal>, +} + +impl ContextMenuRenderer { + pub fn close(&mut self) { + self.menu.set(None); + } + + pub fn render(&self) -> Element { + if let Some(menu) = self.menu.cloned() { + rsx! { + ContextMenu{ + items: menu.items, + left: menu.left, + top: menu.top + } + } + } else { + rsx! {} + } + } } #[component] -pub fn ContextMenu(props: ContextMenuProps) -> Element { +pub fn ContextMenuRoot() -> Element { + let renderer = use_context::(); + rsx! { + {renderer.render()} + } +} + +#[component] +fn ContextMenu(props: props::ContextMenu) -> Element { let menu_items = if let Some(items) = props.items { rsx! { ItemList { items } @@ -29,23 +72,22 @@ pub fn ContextMenu(props: ContextMenuProps) -> Element { } #[component] -fn ItemList(items: Vec) -> Element { +fn ItemList(items: Vec) -> Element { rsx! { div{ class: "itemList", - {items.iter().map(|e| rsx!{{e}}) } + {items.iter().map(|e| rsx!{ + ContextMenuItem{ + name: e.name.clone(), + sub_items: e.sub_items.clone() + } + })} } } } -#[derive(PartialEq, Clone, Props)] -pub struct ContextMenuItemProps { - pub name: String, - pub sub_items: Option>, -} - #[component] -pub fn ContextMenuItem(props: ContextMenuItemProps) -> Element { +fn ContextMenuItem(props: props::ContextMenuItem) -> Element { rsx! { div{ class: "contextItem", diff --git a/AobaClient/src/components/media_grid.rs b/AobaClient/src/components/media_grid.rs index 6b5f26e..8a1fa04 100644 --- a/AobaClient/src/components/media_grid.rs +++ b/AobaClient/src/components/media_grid.rs @@ -2,7 +2,10 @@ use dioxus::prelude::*; use tonic::IntoRequest; use crate::{ - components::{ContextMenu, ContextMenuItem, MediaItem}, + components::{ + ContextMenuRenderer, MediaItem, + props::{ContextMenu, ContextMenuItem}, + }, rpc::{aoba::PageFilter, get_rpc_client}, }; @@ -46,36 +49,32 @@ pub fn MediaGrid(props: MediaGridProps) -> Element { } })); - let mut context_menu: Signal = use_signal(|| rsx! {}); + let mut ct_renderer = use_context::(); + let oncontext = move |event: Event| { event.prevent_default(); let data = event.data(); let pos = data.coordinates().client(); let left = pos.x; let top = pos.y; - context_menu.set(rsx! { - ContextMenu{ - left: left, - top: top, - items: vec![ - rsx!{ - ContextMenuItem{ - name: "Details" - } - }, - rsx!{ - ContextMenuItem{ - name: "Download" - } - }, - rsx!{ - ContextMenuItem{ - name: "Delete" - } - }, - ] - } - }); + ct_renderer.menu.set(Some(ContextMenu { + left: left, + top: top, + items: Some(vec![ + ContextMenuItem { + name: "Details".to_string(), + ..Default::default() + }, + ContextMenuItem { + name: "Download".to_string(), + ..Default::default() + }, + ContextMenuItem { + name: "Delete".to_string(), + ..Default::default() + }, + ]), + })); }; match media_result.cloned() { @@ -83,14 +82,10 @@ pub fn MediaGrid(props: MediaGridProps) -> Element { Ok(result) => rsx! { div { class: "mediaGrid", - onclick: move |_e| { - context_menu.set(rsx!{}); - }, {result.items.iter().map(|itm| rsx!{ MediaItem { item: Some(itm.clone()), oncontextmenu: oncontext } })}, } - {context_menu.cloned()} }, Err(msg) => rsx! { div { diff --git a/AobaClient/src/layouts/main_layout.rs b/AobaClient/src/layouts/main_layout.rs index 8b316fb..9d34555 100644 --- a/AobaClient/src/layouts/main_layout.rs +++ b/AobaClient/src/layouts/main_layout.rs @@ -1,6 +1,11 @@ use dioxus::prelude::*; -use crate::{Route, components::Navbar, contexts::AuthContext, views::Login}; +use crate::{ + Route, + components::{ContextMenuRoot, Navbar}, + contexts::AuthContext, + views::Login, +}; #[component] pub fn MainLayout() -> Element { @@ -13,6 +18,7 @@ pub fn MainLayout() -> Element { } return rsx! { + ContextMenuRoot { } Navbar {} div { id: "content", Outlet:: {} } }; diff --git a/AobaClient/src/main.rs b/AobaClient/src/main.rs index cb5258a..ad2e2e0 100644 --- a/AobaClient/src/main.rs +++ b/AobaClient/src/main.rs @@ -11,6 +11,8 @@ use contexts::AuthContext; use dioxus::prelude::*; use route::Route; +use crate::components::ContextMenuRenderer; + #[cfg(debug_assertions)] pub const HOST: &'static str = "http://localhost:8081"; #[cfg(debug_assertions)] @@ -31,6 +33,7 @@ fn main() { #[component] fn App() -> Element { let _auth_state = use_context_provider(|| AuthContext::new()); + let _renderer = use_context_provider(|| ContextMenuRenderer::default()); rsx! { document::Link { rel: "icon", href: FAVICON } document::Link { rel: "preconnect", href: "https://fonts.googleapis.com" } @@ -41,6 +44,7 @@ fn App() -> Element { rel: "stylesheet", href: "https://fonts.googleapis.com/css2?family=Noto+Sans:ital,wght@0,100..900;1,100..900&display=swap", } - Router:: {} + + Router:: { } } }