ContextMenu Renderer Finalized

This commit is contained in:
2025-08-10 00:04:55 -04:00
parent 6df6de098b
commit e832ccf07e
6 changed files with 97 additions and 75 deletions

View File

@@ -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;

View File

@@ -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<Vec<ContextMenuItem>>,
pub items: Element,
}
#[derive(PartialEq, Clone, Props, Default)]
pub struct ContextMenuItem {
pub name: String,
pub sub_items: Option<Vec<ContextMenuItem>>,
pub sub_items: Option<Element>,
pub onclick: Option<EventHandler<MouseEvent>>,
}
}
#[derive(Clone, Copy, Default)]
pub struct ContextMenuRenderer {
pub menu: Signal<Option<props::ContextMenu>>,
pub menu: Signal<Option<Element>>,
}
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<props::ContextMenuItem>) -> 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::<ContextMenuRenderer>();
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"

View File

@@ -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::<ContextMenuRenderer>();
let oncontext = move |event: Event<MouseData>| {
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()
}
})},
}
},

View File

@@ -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<MediaModel>,
pub oncontextmenu: Option<EventHandler<Event<MouseData>>>,
// pub oncontextmenu: Option<EventHandler<Event<MouseData>>>,
}
#[component]
pub fn MediaItem(props: MediaItemProps) -> Element {
let mut ct_renderer = use_context::<ContextMenuRenderer>();
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<MouseData>| {
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",

View File

@@ -2,7 +2,7 @@ use dioxus::prelude::*;
use crate::{
Route,
components::{ContextMenuRoot, Navbar},
components::{ContextMenuRenderer, ContextMenuRoot, Navbar},
contexts::AuthContext,
views::Login,
};
@@ -17,9 +17,20 @@ pub fn MainLayout() -> Element {
};
}
let mut ct_renderer = use_context::<ContextMenuRenderer>();
return rsx! {
ContextMenuRoot { }
Navbar { }
div { id: "content", Outlet::<Route> {} }
div {
id: "content",
onclick: move |_| {
ct_renderer.close();
},
oncontextmenu: move |_| {
ct_renderer.close();
},
Outlet::<Route> { }
}
};
}

View File

@@ -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" }