Add right-click context menu to external calendars for deletion
Users can now right-click on external calendar items in the sidebar to access a context menu with a "Delete Calendar" option. The delete action removes the calendar from both the server and local state, including all associated events from the calendar display. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1029,6 +1029,39 @@ pub fn App() -> Html {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
})}
|
})}
|
||||||
|
on_external_calendar_delete={Callback::from({
|
||||||
|
let external_calendars = external_calendars.clone();
|
||||||
|
let external_calendar_events = external_calendar_events.clone();
|
||||||
|
move |id: i32| {
|
||||||
|
let external_calendars = external_calendars.clone();
|
||||||
|
let external_calendar_events = external_calendar_events.clone();
|
||||||
|
wasm_bindgen_futures::spawn_local(async move {
|
||||||
|
// Delete the external calendar from the server
|
||||||
|
if let Err(err) = CalendarService::delete_external_calendar(id).await {
|
||||||
|
web_sys::console::log_1(
|
||||||
|
&format!("Failed to delete external calendar: {}", err).into(),
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove calendar from local state
|
||||||
|
let mut calendars = (*external_calendars).clone();
|
||||||
|
calendars.retain(|c| c.id != id);
|
||||||
|
external_calendars.set(calendars.clone());
|
||||||
|
|
||||||
|
// Remove events from this calendar
|
||||||
|
let mut events = (*external_calendar_events).clone();
|
||||||
|
events.retain(|e| {
|
||||||
|
if let Some(ref calendar_path) = e.calendar_path {
|
||||||
|
calendar_path != &format!("external_{}", id)
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
external_calendar_events.set(events);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})}
|
||||||
color_picker_open={(*color_picker_open).clone()}
|
color_picker_open={(*color_picker_open).clone()}
|
||||||
on_color_change={on_color_change}
|
on_color_change={on_color_change}
|
||||||
on_color_picker_toggle={on_color_picker_toggle}
|
on_color_picker_toggle={on_color_picker_toggle}
|
||||||
|
|||||||
@@ -104,6 +104,7 @@ pub struct SidebarProps {
|
|||||||
pub on_create_external_calendar: Callback<()>,
|
pub on_create_external_calendar: Callback<()>,
|
||||||
pub external_calendars: Vec<ExternalCalendar>,
|
pub external_calendars: Vec<ExternalCalendar>,
|
||||||
pub on_external_calendar_toggle: Callback<i32>,
|
pub on_external_calendar_toggle: Callback<i32>,
|
||||||
|
pub on_external_calendar_delete: Callback<i32>,
|
||||||
pub color_picker_open: Option<String>,
|
pub color_picker_open: Option<String>,
|
||||||
pub on_color_change: Callback<(String, String)>,
|
pub on_color_change: Callback<(String, String)>,
|
||||||
pub on_color_picker_toggle: Callback<String>,
|
pub on_color_picker_toggle: Callback<String>,
|
||||||
@@ -119,6 +120,7 @@ pub struct SidebarProps {
|
|||||||
|
|
||||||
#[function_component(Sidebar)]
|
#[function_component(Sidebar)]
|
||||||
pub fn sidebar(props: &SidebarProps) -> Html {
|
pub fn sidebar(props: &SidebarProps) -> Html {
|
||||||
|
let external_context_menu_open = use_state(|| None::<i32>);
|
||||||
let on_view_change = {
|
let on_view_change = {
|
||||||
let on_view_change = props.on_view_change.clone();
|
let on_view_change = props.on_view_change.clone();
|
||||||
Callback::from(move |e: Event| {
|
Callback::from(move |e: Event| {
|
||||||
@@ -158,6 +160,30 @@ pub fn sidebar(props: &SidebarProps) -> Html {
|
|||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let on_external_calendar_context_menu = {
|
||||||
|
let external_context_menu_open = external_context_menu_open.clone();
|
||||||
|
Callback::from(move |(e, cal_id): (MouseEvent, i32)| {
|
||||||
|
e.prevent_default();
|
||||||
|
external_context_menu_open.set(Some(cal_id));
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
let on_external_calendar_delete = {
|
||||||
|
let on_external_calendar_delete = props.on_external_calendar_delete.clone();
|
||||||
|
let external_context_menu_open = external_context_menu_open.clone();
|
||||||
|
Callback::from(move |cal_id: i32| {
|
||||||
|
on_external_calendar_delete.emit(cal_id);
|
||||||
|
external_context_menu_open.set(None);
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
let close_external_context_menu = {
|
||||||
|
let external_context_menu_open = external_context_menu_open.clone();
|
||||||
|
Callback::from(move |_| {
|
||||||
|
external_context_menu_open.set(None);
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
html! {
|
html! {
|
||||||
<aside class="app-sidebar">
|
<aside class="app-sidebar">
|
||||||
<div class="sidebar-header">
|
<div class="sidebar-header">
|
||||||
@@ -228,8 +254,17 @@ pub fn sidebar(props: &SidebarProps) -> Html {
|
|||||||
};
|
};
|
||||||
|
|
||||||
html! {
|
html! {
|
||||||
<li class="external-calendar-item">
|
<li class="external-calendar-item" style="position: relative;">
|
||||||
<div class="external-calendar-info">
|
<div
|
||||||
|
class="external-calendar-info"
|
||||||
|
oncontextmenu={{
|
||||||
|
let on_context_menu = on_external_calendar_context_menu.clone();
|
||||||
|
let cal_id = cal.id;
|
||||||
|
Callback::from(move |e: MouseEvent| {
|
||||||
|
on_context_menu.emit((e, cal_id));
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
>
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={cal.is_visible}
|
checked={cal.is_visible}
|
||||||
@@ -242,6 +277,36 @@ pub fn sidebar(props: &SidebarProps) -> Html {
|
|||||||
<span class="external-calendar-name">{&cal.name}</span>
|
<span class="external-calendar-name">{&cal.name}</span>
|
||||||
<span class="external-calendar-indicator">{"📅"}</span>
|
<span class="external-calendar-indicator">{"📅"}</span>
|
||||||
</div>
|
</div>
|
||||||
|
{
|
||||||
|
if *external_context_menu_open == Some(cal.id) {
|
||||||
|
html! {
|
||||||
|
<>
|
||||||
|
<div
|
||||||
|
class="context-menu-overlay"
|
||||||
|
onclick={close_external_context_menu.clone()}
|
||||||
|
style="position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; z-index: 999;"
|
||||||
|
/>
|
||||||
|
<div class="context-menu" style="position: absolute; top: 0; right: 0; background: white; border: 1px solid #ccc; border-radius: 4px; box-shadow: 0 2px 8px rgba(0,0,0,0.15); z-index: 1000; min-width: 120px;">
|
||||||
|
<div
|
||||||
|
class="context-menu-item"
|
||||||
|
style="padding: 8px 12px; cursor: pointer; color: #d73a49;"
|
||||||
|
onclick={{
|
||||||
|
let on_delete = on_external_calendar_delete.clone();
|
||||||
|
let cal_id = cal.id;
|
||||||
|
Callback::from(move |_| {
|
||||||
|
on_delete.emit(cal_id);
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{"Delete Calendar"}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
html! {}
|
||||||
|
}
|
||||||
|
}
|
||||||
</li>
|
</li>
|
||||||
}
|
}
|
||||||
}).collect::<Html>()
|
}).collect::<Html>()
|
||||||
|
|||||||
Reference in New Issue
Block a user