diff --git a/src/components/week_view.rs b/src/components/week_view.rs index 326de39..9afbd8b 100644 --- a/src/components/week_view.rs +++ b/src/components/week_view.rs @@ -32,6 +32,8 @@ pub struct WeekViewProps { enum DragType { CreateEvent, MoveEvent(CalendarEvent), + ResizeEventStart(CalendarEvent), // Resizing from the top edge (start time) + ResizeEventEnd(CalendarEvent), // Resizing from the bottom edge (end time) } #[derive(Clone, PartialEq)] @@ -234,6 +236,51 @@ pub fn week_view(props: &WeekViewProps) -> Html { let new_start_datetime = NaiveDateTime::new(current_drag.start_date, new_start_time); let new_end_datetime = new_start_datetime + original_duration; + if let Some(callback) = &on_event_update { + callback.emit((event.clone(), new_start_datetime, new_end_datetime)); + } + }, + DragType::ResizeEventStart(event) => { + // Calculate new start time based on drag position + let new_start_time = pixels_to_time(current_drag.current_y); + + // Keep the original end time + let original_end = if let Some(end) = event.end { + end.with_timezone(&chrono::Local).naive_local() + } else { + // If no end time, use start time + 1 hour as default + event.start.with_timezone(&chrono::Local).naive_local() + chrono::Duration::hours(1) + }; + + let new_start_datetime = NaiveDateTime::new(current_drag.start_date, new_start_time); + + // Ensure start is before end (minimum 15 minutes) + let new_end_datetime = if new_start_datetime >= original_end { + new_start_datetime + chrono::Duration::minutes(15) + } else { + original_end + }; + + if let Some(callback) = &on_event_update { + callback.emit((event.clone(), new_start_datetime, new_end_datetime)); + } + }, + DragType::ResizeEventEnd(event) => { + // Calculate new end time based on drag position + let new_end_time = pixels_to_time(current_drag.current_y); + + // Keep the original start time + let original_start = event.start.with_timezone(&chrono::Local).naive_local(); + + let new_end_datetime = NaiveDateTime::new(current_drag.start_date, new_end_time); + + // Ensure end is after start (minimum 15 minutes) + let new_start_datetime = if new_end_datetime <= original_start { + new_end_datetime - chrono::Duration::minutes(15) + } else { + original_start + }; + if let Some(callback) = &on_event_update { callback.emit((event.clone(), new_start_datetime, new_end_datetime)); } @@ -301,24 +348,22 @@ pub fn week_view(props: &WeekViewProps) -> Html { Callback::from(move |e: MouseEvent| { e.stop_propagation(); // Prevent drag-to-create from starting on event clicks - // Only handle left-click (button 0) + // Only handle left-click (button 0) for moving if e.button() != 0 { return; } - // Calculate Y position relative to the day column - let relative_y = e.layer_y() as f64; - let relative_y = if relative_y > 0.0 { relative_y } else { e.offset_y() as f64 }; + // 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 }; - // Get event's current position + // Get event's current position for offset calculation let (event_start_pixels, _, _) = calculate_event_position(&event_for_drag, date_for_drag); let event_start_pixels = event_start_pixels as f64; - - // Calculate offset from the top of the event - let offset_y = relative_y - event_start_pixels; + let offset_y = absolute_y - event_start_pixels; // Snap to increment - let snapped_y = snap_to_increment(relative_y, time_increment); + let snapped_y = snap_to_increment(absolute_y, time_increment); drag_state.set(Some(DragState { is_dragging: true, @@ -336,7 +381,16 @@ pub fn week_view(props: &WeekViewProps) -> Html { if let Some(callback) = &props.on_event_context_menu { let callback = callback.clone(); let event = event.clone(); + let drag_state_for_context = drag_state.clone(); Some(Callback::from(move |e: web_sys::MouseEvent| { + // Check if we're currently dragging - if so, prevent context menu + if let Some(drag) = (*drag_state_for_context).clone() { + if drag.is_dragging { + e.prevent_default(); + return; + } + } + e.prevent_default(); e.stop_propagation(); // Prevent calendar context menu from also triggering callback.emit((e, event.clone())); @@ -377,12 +431,16 @@ pub fn week_view(props: &WeekViewProps) -> Html { } }; - // Check if this event is currently being dragged + // Check if this event is currently being dragged or resized let is_being_dragged = if let Some(drag) = (*drag_state).clone() { - if let DragType::MoveEvent(dragged_event) = &drag.drag_type { - dragged_event.uid == event.uid && drag.is_dragging - } else { - false + match &drag.drag_type { + DragType::MoveEvent(dragged_event) => + dragged_event.uid == event.uid && drag.is_dragging, + DragType::ResizeEventStart(dragged_event) => + dragged_event.uid == event.uid && drag.is_dragging, + DragType::ResizeEventEnd(dragged_event) => + dragged_event.uid == event.uid && drag.is_dragging, + _ => false, } } else { false @@ -392,6 +450,55 @@ pub fn week_view(props: &WeekViewProps) -> Html { // Hide the original event while being dragged Some(html! {}) } else { + // Create resize handles for left-click resize + let resize_start_handler = { + let drag_state = drag_state.clone(); + let event_for_resize = event.clone(); + let date_for_drag = *date; + let time_increment = props.time_increment; + Callback::from(move |e: web_sys::MouseEvent| { + e.stop_propagation(); + + 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 snapped_y = snap_to_increment(relative_y, time_increment); + + drag_state.set(Some(DragState { + is_dragging: true, + drag_type: DragType::ResizeEventStart(event_for_resize.clone()), + start_date: date_for_drag, + start_y: snapped_y, + current_y: snapped_y, + offset_y: 0.0, + })); + e.prevent_default(); + }) + }; + + let resize_end_handler = { + let drag_state = drag_state.clone(); + let event_for_resize = event.clone(); + let date_for_drag = *date; + let time_increment = props.time_increment; + Callback::from(move |e: web_sys::MouseEvent| { + e.stop_propagation(); + + 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 snapped_y = snap_to_increment(relative_y, time_increment); + + drag_state.set(Some(DragState { + is_dragging: true, + drag_type: DragType::ResizeEventEnd(event_for_resize.clone()), + start_date: date_for_drag, + start_y: snapped_y, + current_y: snapped_y, + offset_y: 0.0, + })); + e.prevent_default(); + }) + }; + Some(html! {