From 927cd7d2bb56771675bec8652b435707a7356cd9 Mon Sep 17 00:00:00 2001 From: Connor Johnstone Date: Fri, 5 Sep 2025 12:17:09 -0400 Subject: [PATCH] Add color picker functionality to external calendars MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Enable clicking external calendar color icons to open color picker dropdown - Implement backend API integration for updating external calendar colors - Add conditional hover effects to prevent interference with color picker - Use extremely high z-index (999999) to ensure dropdown appears above all elements - Match existing CalDAV calendar color picker behavior and styling - Support real-time color updates with immediate visual feedback - Maintain color consistency across sidebar and calendar events 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- frontend/src/app.rs | 59 +++++++++++++++++++++++++----- frontend/src/components/sidebar.rs | 49 ++++++++++++++++++++++++- frontend/styles.css | 4 +- 3 files changed, 99 insertions(+), 13 deletions(-) diff --git a/frontend/src/app.rs b/frontend/src/app.rs index c06d85a..2110575 100644 --- a/frontend/src/app.rs +++ b/frontend/src/app.rs @@ -567,19 +567,60 @@ pub fn App() -> Html { let on_color_change = { let user_info = user_info.clone(); + let external_calendars = external_calendars.clone(); let color_picker_open = color_picker_open.clone(); Callback::from(move |(calendar_path, color): (String, String)| { - if let Some(mut info) = (*user_info).clone() { - for calendar in &mut info.calendars { - if calendar.path == calendar_path { - calendar.color = color.clone(); - break; - } + if calendar_path.starts_with("external_") { + // Handle external calendar color change + if let Ok(id_str) = calendar_path.strip_prefix("external_").unwrap_or("").parse::() { + let external_calendars = external_calendars.clone(); + let color = color.clone(); + + wasm_bindgen_futures::spawn_local(async move { + // Find the external calendar to get its current details + if let Some(cal) = (*external_calendars).iter().find(|c| c.id == id_str) { + match CalendarService::update_external_calendar( + id_str, + &cal.name, + &cal.url, + &color, + cal.is_visible, + ).await { + Ok(_) => { + // Update the local state + let mut updated_calendars = (*external_calendars).clone(); + for calendar in &mut updated_calendars { + if calendar.id == id_str { + calendar.color = color.clone(); + break; + } + } + external_calendars.set(updated_calendars); + + // No need to refresh events - they will automatically pick up the new color + // from the calendar when rendered since they use the same calendar_path matching + } + Err(e) => { + web_sys::console::error_1(&format!("Failed to update external calendar color: {}", e).into()); + } + } + } + }); } - user_info.set(Some(info.clone())); + } else { + // Handle CalDAV calendar color change (existing logic) + if let Some(mut info) = (*user_info).clone() { + for calendar in &mut info.calendars { + if calendar.path == calendar_path { + calendar.color = color.clone(); + break; + } + } + user_info.set(Some(info.clone())); - if let Ok(json) = serde_json::to_string(&info) { - let _ = LocalStorage::set("calendar_colors", json); + if let Ok(json) = serde_json::to_string(&info) { + let _ = LocalStorage::set("calendar_colors", json); + } } } color_picker_open.set(None); diff --git a/frontend/src/components/sidebar.rs b/frontend/src/components/sidebar.rs index d850962..5a6deae 100644 --- a/frontend/src/components/sidebar.rs +++ b/frontend/src/components/sidebar.rs @@ -256,7 +256,11 @@ pub fn sidebar(props: &SidebarProps) -> Html { html! {
  • Html { + onclick={{ + let on_color_picker_toggle = props.on_color_picker_toggle.clone(); + let external_id = format!("external_{}", cal.id); + Callback::from(move |e: MouseEvent| { + e.stop_propagation(); + on_color_picker_toggle.emit(external_id.clone()); + }) + }} + > + { + if props.color_picker_open.as_ref() == Some(&format!("external_{}", cal.id)) { + html! { +
    + { + props.available_colors.iter().map(|color| { + let color_str = color.clone(); + let external_id = format!("external_{}", cal.id); + let on_color_change = props.on_color_change.clone(); + + let on_color_select = Callback::from(move |_: MouseEvent| { + on_color_change.emit((external_id.clone(), color_str.clone())); + }); + + let is_selected = cal.color == *color; + + html! { +
    + } + }).collect::() + } +
    + } + } else { + html! {} + } + } + {&cal.name}
    { diff --git a/frontend/styles.css b/frontend/styles.css index 6469969..1e1cc13 100644 --- a/frontend/styles.css +++ b/frontend/styles.css @@ -281,7 +281,7 @@ body { border-radius: 4px; padding: 1rem; box-shadow: 0 4px 12px rgba(0,0,0,0.15); - z-index: 1000; + z-index: 999999; display: grid; grid-template-columns: repeat(4, 1fr); gap: 0.75rem; @@ -3677,7 +3677,7 @@ body { border: 1px solid var(--glass-bg); } -.external-calendar-info:hover { +.external-calendar-info:hover:not(.color-picker-active) { background: var(--glass-bg); border-color: var(--glass-bg-light); transform: translateX(2px);