use crate::models::ical::VEvent; use web_sys::MouseEvent; use yew::prelude::*; #[derive(Clone, PartialEq, Debug)] pub enum DeleteAction { DeleteThis, DeleteFollowing, DeleteSeries, } #[derive(Clone, PartialEq, Debug)] pub enum EditAction { EditThis, EditFuture, EditAll, } #[derive(Properties, PartialEq)] pub struct EventContextMenuProps { pub is_open: bool, pub x: i32, pub y: i32, pub event: Option, pub on_edit: Callback, pub on_delete: Callback, pub on_close: Callback<()>, } #[function_component(EventContextMenu)] pub fn event_context_menu(props: &EventContextMenuProps) -> Html { let menu_ref = use_node_ref(); if !props.is_open { return html! {}; } // Smart positioning to keep menu within viewport let (x, y) = { let mut x = props.x; let mut y = props.y; // Try to get actual viewport dimensions if let Some(window) = web_sys::window() { if let (Ok(width), Ok(height)) = (window.inner_width(), window.inner_height()) { if let (Some(w), Some(h)) = (width.as_f64(), height.as_f64()) { let viewport_width = w as i32; let viewport_height = h as i32; // More accurate menu dimensions based on actual CSS and content let menu_width = if props.event.as_ref().map_or(false, |e| e.rrule.is_some()) { 280 // Recurring: "Edit This and Future Events" is long text + padding } else { 180 // Non-recurring: "Edit Event" + "Delete Event" + padding }; let menu_height = if props.event.as_ref().map_or(false, |e| e.rrule.is_some()) { 200 // 6 items × ~32px per item (12px padding top/bottom + text height + borders) } else { 100 // 2 items × ~32px per item + some extra margin }; // Adjust horizontally if too close to right edge if x + menu_width > viewport_width - 10 { x = x.saturating_sub(menu_width); } // Adjust vertically if too close to bottom edge if y + menu_height > viewport_height - 10 { y = y.saturating_sub(menu_height); } // Ensure minimum margins from edges x = x.max(5); y = y.max(5); } } } (x, y) }; let style = format!( "position: fixed; left: {}px; top: {}px; z-index: 1001;", x, y ); // Check if the event is recurring let is_recurring = props .event .as_ref() .map(|event| event.rrule.is_some()) .unwrap_or(false); let create_edit_callback = |action: EditAction| { let on_edit = props.on_edit.clone(); let on_close = props.on_close.clone(); Callback::from(move |_: MouseEvent| { on_edit.emit(action.clone()); on_close.emit(()); }) }; let create_delete_callback = |action: DeleteAction| { let on_delete = props.on_delete.clone(); let on_close = props.on_close.clone(); Callback::from(move |_: MouseEvent| { on_delete.emit(action.clone()); on_close.emit(()); }) }; html! {
{ if is_recurring { html! { <>
{"Edit This Event"}
{"Edit This and Future Events"}
{"Edit All Events in Series"}
} } else { html! {
{"Edit Event"}
} } } { if is_recurring { html! { <>
{"Delete This Event"}
{"Delete Following Events"}
{"Delete Entire Series"}
} } else { html! {
{"Delete Event"}
} } }
} }