Add color picker functionality to external calendars

- 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 <noreply@anthropic.com>
This commit is contained in:
Connor Johnstone
2025-09-05 12:17:09 -04:00
parent 38b22287c7
commit 927cd7d2bb
3 changed files with 99 additions and 13 deletions

View File

@@ -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::<i32>() {
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);

View File

@@ -256,7 +256,11 @@ pub fn sidebar(props: &SidebarProps) -> Html {
html! {
<li class="external-calendar-item" style="position: relative;">
<div
class="external-calendar-info"
class={if props.color_picker_open.as_ref() == Some(&format!("external_{}", cal.id)) {
"external-calendar-info color-picker-active"
} else {
"external-calendar-info"
}}
oncontextmenu={{
let on_context_menu = on_external_calendar_context_menu.clone();
let cal_id = cal.id;
@@ -273,7 +277,48 @@ pub fn sidebar(props: &SidebarProps) -> Html {
<span
class="external-calendar-color"
style={format!("background-color: {}", cal.color)}
/>
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! {
<div class="color-picker-dropdown">
{
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! {
<div
key={color.clone()}
class={if is_selected { "color-option selected" } else { "color-option" }}
style={format!("background-color: {}", color)}
onclick={on_color_select}
/>
}
}).collect::<Html>()
}
</div>
}
} else {
html! {}
}
}
</span>
<span class="external-calendar-name">{&cal.name}</span>
<div class="external-calendar-actions">
{

View File

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