diff --git a/AobaClient/assets/style/main.scss b/AobaClient/assets/style/main.scss index 0c452ae..96d1289 100644 --- a/AobaClient/assets/style/main.scss +++ b/AobaClient/assets/style/main.scss @@ -176,6 +176,11 @@ form { grid-template-columns: $size 1fr auto; height: $size; transition: border 0.1s ease-out; + cursor: default; + + &.clickable { + cursor: pointer; + } .label { display: flex; diff --git a/AobaClient/src/components/context_menu.rs b/AobaClient/src/components/context_menu.rs index 7c1738b..8eabd50 100644 --- a/AobaClient/src/components/context_menu.rs +++ b/AobaClient/src/components/context_menu.rs @@ -2,26 +2,27 @@ use core::str; use dioxus::prelude::*; -pub mod props { +mod props { use dioxus::prelude::*; #[derive(PartialEq, Clone, Props)] pub struct ContextMenu { pub top: f64, pub left: f64, - pub items: Option>, + pub items: Element, } #[derive(PartialEq, Clone, Props, Default)] pub struct ContextMenuItem { pub name: String, - pub sub_items: Option>, + pub sub_items: Option, + pub onclick: Option>, } } #[derive(Clone, Copy, Default)] pub struct ContextMenuRenderer { - pub menu: Signal>, + pub menu: Signal>, } impl ContextMenuRenderer { @@ -32,11 +33,7 @@ impl ContextMenuRenderer { pub fn render(&self) -> Element { if let Some(menu) = self.menu.cloned() { rsx! { - ContextMenu{ - items: menu.items, - left: menu.left, - top: menu.top - } + {menu} } } else { rsx! {} @@ -53,43 +50,40 @@ pub fn ContextMenuRoot() -> Element { } #[component] -fn ContextMenu(props: props::ContextMenu) -> Element { - let menu_items = if let Some(items) = props.items { - rsx! { - ItemList { items } - } - } else { - rsx! {} - }; - +pub fn ContextMenu(props: props::ContextMenu) -> Element { rsx! { div { class: "contextMenu", style: "left: {props.left}px; top: {props.top}px;", - {menu_items} + ItemList { items: props.items } } } } #[component] -fn ItemList(items: Vec) -> Element { +fn ItemList(items: Element) -> Element { rsx! { div{ class: "itemList", - {items.iter().map(|e| rsx!{ - ContextMenuItem{ - name: e.name.clone(), - sub_items: e.sub_items.clone() - } - })} + {items} } } } #[component] -fn ContextMenuItem(props: props::ContextMenuItem) -> Element { +pub fn ContextMenuItem(props: props::ContextMenuItem) -> Element { + let mut renderer = use_context::(); + if let Some(_sub) = props.sub_items { + todo!("Sub Menu"); + } rsx! { div{ + onclick: move |e|{ + if let Some(handler) = props.onclick{ + handler.call(e); + } + renderer.close(); + }, class: "contextItem", div { class: "icon" diff --git a/AobaClient/src/components/media_grid.rs b/AobaClient/src/components/media_grid.rs index 8a1fa04..4d947c8 100644 --- a/AobaClient/src/components/media_grid.rs +++ b/AobaClient/src/components/media_grid.rs @@ -2,10 +2,7 @@ use dioxus::prelude::*; use tonic::IntoRequest; use crate::{ - components::{ - ContextMenuRenderer, MediaItem, - props::{ContextMenu, ContextMenuItem}, - }, + components::MediaItem, rpc::{aoba::PageFilter, get_rpc_client}, }; @@ -49,41 +46,16 @@ pub fn MediaGrid(props: MediaGridProps) -> Element { } })); - 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; - 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() { Some(value) => match value { Ok(result) => rsx! { div { class: "mediaGrid", + // oncontextmenu: oncontext, {result.items.iter().map(|itm| rsx!{ - MediaItem { item: Some(itm.clone()), oncontextmenu: oncontext } + MediaItem { + item: itm.clone() + } })}, } }, @@ -100,7 +72,7 @@ pub fn MediaGrid(props: MediaGridProps) -> Element { div{ class: "mediaGrid", {(0..50).map(|_| rsx!{ - MediaItem {} + MediaItem { } })} } }, diff --git a/AobaClient/src/components/media_item.rs b/AobaClient/src/components/media_item.rs index 801734e..e0bd11f 100644 --- a/AobaClient/src/components/media_item.rs +++ b/AobaClient/src/components/media_item.rs @@ -1,32 +1,72 @@ use dioxus::prelude::*; +use web_sys::window; -use crate::{HOST, rpc::aoba::MediaModel}; +use crate::{ + HOST, + components::{ContextMenu, ContextMenuItem, ContextMenuRenderer}, + rpc::aoba::MediaModel, +}; #[derive(PartialEq, Clone, Props)] pub struct MediaItemProps { pub item: Option, - pub oncontextmenu: Option>>, + // pub oncontextmenu: Option>>, } #[component] pub fn MediaItem(props: MediaItemProps) -> Element { + let mut ct_renderer = use_context::(); + if let Some(item) = props.item { let mtype = item.media_type().as_str_name(); let filename = item.file_name; let id = item.id.unwrap().value; let thumb = item.thumb_url; let url = item.media_url; + let download = format!("{HOST}{url}"); + + let oncontext = move |event: Event| { + println!("ContextMenu"); + event.prevent_default(); + event.stop_propagation(); + let data = event.data(); + if data.modifiers().ctrl() { + return; + } + let pos = data.coordinates().client(); + let left = pos.x; + let top = pos.y; + let download = download.clone(); + + let menu: Element = rsx! { + ContextMenu { + left: left, + top: top, + items: rsx! { + ContextMenuItem { + name: "Details", + }, + ContextMenuItem { + name: "Download", + onclick: move |_|{ + _ = window().unwrap().open_with_url_and_target(&download, "_blank"); + } + }, + ContextMenuItem { + name: "Delete", + }, + }, + } + }; + ct_renderer.menu.set(Some(menu)); + }; return rsx! { a { class: "mediaItem", - href: "{HOST}/{url}", + href: "{HOST}{url}", target: "_blank", - oncontextmenu: move |e| { - if let Some(handler) = props.oncontextmenu{ - handler.call(e); - } - }, + oncontextmenu: oncontext, "data-id" : id, img { src: "{HOST}{thumb}" } span { class: "info", diff --git a/AobaClient/src/layouts/main_layout.rs b/AobaClient/src/layouts/main_layout.rs index 9d34555..76dd84b 100644 --- a/AobaClient/src/layouts/main_layout.rs +++ b/AobaClient/src/layouts/main_layout.rs @@ -2,7 +2,7 @@ use dioxus::prelude::*; use crate::{ Route, - components::{ContextMenuRoot, Navbar}, + components::{ContextMenuRenderer, ContextMenuRoot, Navbar}, contexts::AuthContext, views::Login, }; @@ -13,13 +13,24 @@ pub fn MainLayout() -> Element { if auth_context.jwt.cloned().is_none() { return rsx! { - Login {} + Login { } }; } + let mut ct_renderer = use_context::(); + return rsx! { ContextMenuRoot { } - Navbar {} - div { id: "content", Outlet:: {} } + Navbar { } + div { + id: "content", + onclick: move |_| { + ct_renderer.close(); + }, + oncontextmenu: move |_| { + ct_renderer.close(); + }, + Outlet:: { } + } }; } diff --git a/AobaClient/src/main.rs b/AobaClient/src/main.rs index ad2e2e0..5fe0f79 100644 --- a/AobaClient/src/main.rs +++ b/AobaClient/src/main.rs @@ -32,8 +32,8 @@ fn main() { #[component] fn App() -> Element { - let _auth_state = use_context_provider(|| AuthContext::new()); - let _renderer = use_context_provider(|| ContextMenuRenderer::default()); + use_context_provider(|| AuthContext::new()); + use_context_provider(|| ContextMenuRenderer::default()); rsx! { document::Link { rel: "icon", href: FAVICON } document::Link { rel: "preconnect", href: "https://fonts.googleapis.com" }