Fix drag positioning to accurately follow mouse cursor in week view
- Fix coordinate system mismatch between event clicks and drag preview - Convert event-relative coordinates to day-column-relative coordinates - Add movement threshold to prevent accidental drag updates on clicks - Ensure dragged events maintain proper offset from click position 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -44,6 +44,7 @@ struct DragState {
|
||||
start_y: f64,
|
||||
current_y: f64,
|
||||
offset_y: f64, // For event moves, this is the offset from the event's top
|
||||
has_moved: bool, // Track if we've moved enough to constitute a real drag
|
||||
}
|
||||
|
||||
#[function_component(WeekView)]
|
||||
@@ -163,6 +164,7 @@ pub fn week_view(props: &WeekViewProps) -> Html {
|
||||
start_y: snapped_y,
|
||||
current_y: snapped_y,
|
||||
offset_y: 0.0,
|
||||
has_moved: false,
|
||||
}));
|
||||
e.prevent_default();
|
||||
})
|
||||
@@ -175,11 +177,21 @@ pub fn week_view(props: &WeekViewProps) -> Html {
|
||||
if let Some(mut current_drag) = (*drag_state).clone() {
|
||||
if current_drag.is_dragging {
|
||||
// Use layer_y for consistent coordinate calculation
|
||||
let relative_y = e.layer_y() as f64;
|
||||
let relative_y = if relative_y > 0.0 { relative_y } else { e.offset_y() as f64 };
|
||||
let mouse_y = e.layer_y() as f64;
|
||||
let mouse_y = if mouse_y > 0.0 { mouse_y } else { e.offset_y() as f64 };
|
||||
|
||||
// For move operations, we now follow the mouse directly since we start at click position
|
||||
// For resize operations, we still use the mouse position directly
|
||||
let adjusted_y = mouse_y;
|
||||
|
||||
// Snap to increment
|
||||
let snapped_y = snap_to_increment(relative_y, time_increment);
|
||||
let snapped_y = snap_to_increment(adjusted_y, time_increment);
|
||||
|
||||
// Check if we've moved enough to constitute a real drag (5 pixels minimum)
|
||||
let movement_distance = (snapped_y - current_drag.start_y).abs();
|
||||
if movement_distance > 5.0 {
|
||||
current_drag.has_moved = true;
|
||||
}
|
||||
|
||||
current_drag.current_y = snapped_y;
|
||||
drag_state.set(Some(current_drag));
|
||||
@@ -194,7 +206,7 @@ pub fn week_view(props: &WeekViewProps) -> Html {
|
||||
let on_event_update = props.on_event_update.clone();
|
||||
Callback::from(move |_e: MouseEvent| {
|
||||
if let Some(current_drag) = (*drag_state).clone() {
|
||||
if current_drag.is_dragging {
|
||||
if current_drag.is_dragging && current_drag.has_moved {
|
||||
match ¤t_drag.drag_type {
|
||||
DragType::CreateEvent => {
|
||||
// Calculate start and end times
|
||||
@@ -223,8 +235,9 @@ pub fn week_view(props: &WeekViewProps) -> Html {
|
||||
}
|
||||
},
|
||||
DragType::MoveEvent(event) => {
|
||||
// Calculate new start time based on drag position
|
||||
let new_start_time = pixels_to_time(current_drag.current_y);
|
||||
// Calculate new start time based on drag position (accounting for click offset)
|
||||
let event_top_position = current_drag.current_y - current_drag.offset_y;
|
||||
let new_start_time = pixels_to_time(event_top_position);
|
||||
|
||||
// Calculate duration from original event
|
||||
let original_duration = if let Some(end) = event.end {
|
||||
@@ -344,7 +357,7 @@ pub fn week_view(props: &WeekViewProps) -> Html {
|
||||
let drag_state = drag_state.clone();
|
||||
let event_for_drag = event.clone();
|
||||
let date_for_drag = *date;
|
||||
let time_increment = props.time_increment;
|
||||
let _time_increment = props.time_increment;
|
||||
Callback::from(move |e: MouseEvent| {
|
||||
e.stop_propagation(); // Prevent drag-to-create from starting on event clicks
|
||||
|
||||
@@ -353,25 +366,30 @@ pub fn week_view(props: &WeekViewProps) -> Html {
|
||||
return;
|
||||
}
|
||||
|
||||
// Calculate absolute Y position for drag calculations
|
||||
let absolute_y = e.layer_y() as f64;
|
||||
let absolute_y = if absolute_y > 0.0 { absolute_y } else { e.offset_y() as f64 };
|
||||
// Calculate click position relative to event element
|
||||
let click_y_relative = e.layer_y() as f64;
|
||||
let click_y_relative = if click_y_relative > 0.0 { click_y_relative } else { e.offset_y() as f64 };
|
||||
|
||||
// Get event's current position for offset calculation
|
||||
// Get event's current position in day column coordinates
|
||||
let (event_start_pixels, _, _) = calculate_event_position(&event_for_drag, date_for_drag);
|
||||
let event_start_pixels = event_start_pixels as f64;
|
||||
let offset_y = absolute_y - event_start_pixels;
|
||||
|
||||
// Snap to increment
|
||||
let snapped_y = snap_to_increment(absolute_y, time_increment);
|
||||
// Convert click position to day column coordinates
|
||||
let click_y = event_start_pixels + click_y_relative;
|
||||
|
||||
// Store the offset from the event's top where the user clicked
|
||||
// This will be used to maintain the relative click position
|
||||
let offset_y = click_y_relative;
|
||||
|
||||
// Start drag tracking from where we clicked (in day column coordinates)
|
||||
drag_state.set(Some(DragState {
|
||||
is_dragging: true,
|
||||
drag_type: DragType::MoveEvent(event_for_drag.clone()),
|
||||
start_date: date_for_drag,
|
||||
start_y: snapped_y,
|
||||
current_y: snapped_y,
|
||||
start_y: click_y,
|
||||
current_y: click_y,
|
||||
offset_y,
|
||||
has_moved: false,
|
||||
}));
|
||||
e.prevent_default();
|
||||
})
|
||||
@@ -470,6 +488,7 @@ pub fn week_view(props: &WeekViewProps) -> Html {
|
||||
start_y: snapped_y,
|
||||
current_y: snapped_y,
|
||||
offset_y: 0.0,
|
||||
has_moved: false,
|
||||
}));
|
||||
e.prevent_default();
|
||||
})
|
||||
@@ -494,6 +513,7 @@ pub fn week_view(props: &WeekViewProps) -> Html {
|
||||
start_y: snapped_y,
|
||||
current_y: snapped_y,
|
||||
offset_y: 0.0,
|
||||
has_moved: false,
|
||||
}));
|
||||
e.prevent_default();
|
||||
})
|
||||
@@ -580,8 +600,9 @@ pub fn week_view(props: &WeekViewProps) -> Html {
|
||||
}
|
||||
},
|
||||
DragType::MoveEvent(event) => {
|
||||
// Show the event being moved at its new position
|
||||
let new_start_time = pixels_to_time(drag.current_y);
|
||||
// Calculate the event's new position accounting for click offset
|
||||
let preview_position = drag.current_y - drag.offset_y;
|
||||
let new_start_time = pixels_to_time(preview_position);
|
||||
let original_duration = if let Some(end) = event.end {
|
||||
end.signed_duration_since(event.start)
|
||||
} else {
|
||||
@@ -595,7 +616,7 @@ pub fn week_view(props: &WeekViewProps) -> Html {
|
||||
html! {
|
||||
<div
|
||||
class="temp-event-box moving-event"
|
||||
style={format!("top: {}px; height: {}px; background-color: {}; opacity: 0.7;", drag.current_y, duration_pixels, event_color)}
|
||||
style={format!("top: {}px; height: {}px; background-color: {}; opacity: 0.7;", preview_position, duration_pixels, event_color)}
|
||||
>
|
||||
<div class="event-title">{event.summary.as_ref().unwrap_or(&"Untitled".to_string())}</div>
|
||||
<div class="event-time">{format!("{} - {}", new_start_time.format("%I:%M %p"), new_end_time.format("%I:%M %p"))}</div>
|
||||
|
||||
Reference in New Issue
Block a user