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:
@@ -567,8 +567,48 @@ pub fn App() -> Html {
|
|||||||
|
|
||||||
let on_color_change = {
|
let on_color_change = {
|
||||||
let user_info = user_info.clone();
|
let user_info = user_info.clone();
|
||||||
|
let external_calendars = external_calendars.clone();
|
||||||
let color_picker_open = color_picker_open.clone();
|
let color_picker_open = color_picker_open.clone();
|
||||||
Callback::from(move |(calendar_path, color): (String, String)| {
|
Callback::from(move |(calendar_path, color): (String, String)| {
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Handle CalDAV calendar color change (existing logic)
|
||||||
if let Some(mut info) = (*user_info).clone() {
|
if let Some(mut info) = (*user_info).clone() {
|
||||||
for calendar in &mut info.calendars {
|
for calendar in &mut info.calendars {
|
||||||
if calendar.path == calendar_path {
|
if calendar.path == calendar_path {
|
||||||
@@ -582,6 +622,7 @@ pub fn App() -> Html {
|
|||||||
let _ = LocalStorage::set("calendar_colors", json);
|
let _ = LocalStorage::set("calendar_colors", json);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
color_picker_open.set(None);
|
color_picker_open.set(None);
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -256,7 +256,11 @@ pub fn sidebar(props: &SidebarProps) -> Html {
|
|||||||
html! {
|
html! {
|
||||||
<li class="external-calendar-item" style="position: relative;">
|
<li class="external-calendar-item" style="position: relative;">
|
||||||
<div
|
<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={{
|
oncontextmenu={{
|
||||||
let on_context_menu = on_external_calendar_context_menu.clone();
|
let on_context_menu = on_external_calendar_context_menu.clone();
|
||||||
let cal_id = cal.id;
|
let cal_id = cal.id;
|
||||||
@@ -273,7 +277,48 @@ pub fn sidebar(props: &SidebarProps) -> Html {
|
|||||||
<span
|
<span
|
||||||
class="external-calendar-color"
|
class="external-calendar-color"
|
||||||
style={format!("background-color: {}", cal.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>
|
<span class="external-calendar-name">{&cal.name}</span>
|
||||||
<div class="external-calendar-actions">
|
<div class="external-calendar-actions">
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -281,7 +281,7 @@ body {
|
|||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
|
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
|
||||||
z-index: 1000;
|
z-index: 999999;
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(4, 1fr);
|
grid-template-columns: repeat(4, 1fr);
|
||||||
gap: 0.75rem;
|
gap: 0.75rem;
|
||||||
@@ -3677,7 +3677,7 @@ body {
|
|||||||
border: 1px solid var(--glass-bg);
|
border: 1px solid var(--glass-bg);
|
||||||
}
|
}
|
||||||
|
|
||||||
.external-calendar-info:hover {
|
.external-calendar-info:hover:not(.color-picker-active) {
|
||||||
background: var(--glass-bg);
|
background: var(--glass-bg);
|
||||||
border-color: var(--glass-bg-light);
|
border-color: var(--glass-bg-light);
|
||||||
transform: translateX(2px);
|
transform: translateX(2px);
|
||||||
|
|||||||
Reference in New Issue
Block a user